US Mass Shootings
US Mass Shootings

Fuente de los datos

He obtenido el conjunto de datos de Kaggle: US Mass Shootings

La versión utilizada para este proyecto es: Mass Shootings Dataset Ver 5

Introducción al Proyecto sobre Tiroteos Masivos en Estados Unidos (1966-2017)

Este proyecto se basa en el análisis de un conjunto de datos que abarca las trágicas incidencias de tiroteos masivos en Estados Unidos de América entre 1966 y 2017. A lo largo de este periodo de más de cinco décadas, Estados Unidos ha sido escenario de 398 tiroteos masivos, que han dejado una cifra de 1,996 muertos y 2,488 heridos. El análisis de este fenómeno no solo es pertinente por su impacto social y humanitario, sino también por las implicaciones políticas y de política pública que conlleva.

Este conjunto de datos detalla cada uno de estos incidentes violentos, proporcionando no solo cifras de víctimas, sino también información contextual crítica como la ubicación, fecha, resumen del incidente, y variables demográficas y psicológicas de los atacantes, incluyendo su raza, género y estado de salud mental. Además, incluye información geográfica precisa, lo que permite un análisis espacial detallado.

El propósito de este proyecto es utilizar técnicas de minería de texto y análisis de redes sociales para extraer insights más profundos de los datos. La minería de texto nos permitirá entender mejor las circunstancias y características asociadas a estos eventos. Por otro lado, el análisis de redes sociales ayudará a visualizar y analizar las relaciones y patrones que emergen de los datos, como las conexiones entre incidentes basadas en características comunes o patrones de victimización.

Instalación y Carga de Paquetes Necesarios, Directorio de Trabajo

Cargamos librerías necesarias y establecemos el directorio de trabajo:

# Librerias
library(quanteda)  # Para el análisis cuantitativo de datos textuales
library(quanteda.textplots)  # Para visualizaciones de datos textuales dentro de 'quanteda'
library(quanteda.textmodels) # Para modelado de textos con 'quanteda'
library(quanteda.textstats)  # Para generar estadísticas textuales con 'quanteda'
library(stringr)  # Para manipulación de cadenas de texto 
library(tidyverse)  # Colección de paquetes para ciencia de datos
library(tidytext) # Para el análisis de texto dentro del 'tidy data'
library(dplyr)    # Para manipulación de datos
library(scales)  # Biblioteca para funciones de formato de escala en visualizaciones
library(forcats)  # Para trabajar con factores
library(ggplot2)  # Sistema de visualización de datos basado en la gramática de gráficos
library(RColorBrewer)  # Para paletas de colores en gráficos, basado en Color Brewer
library(topicmodels)  # Para modelado de tópicos de datos textuales, incluyendo LDA
library(wordcloud)  # Para generar nubes de palabras
library(SentimentAnalysis)  # Para análisis de sentimientos basado en diccionarios y modelos
library(quanteda.dictionaries)  # Diccionarios predefinidos para su uso con 'quanteda'
library(textdata)  # Para descargar y cargar conjuntos de datos textuales y léxicos
library(igraph)      # Para el manejo de grafos
# Directorio de trabajo
setwd("D:/Laura - UTAD/Tercero/Segundo cuatrimestre/Búsqueda y Análisis de la Información/Tareas/Tarea final")

Lectura de los datos

# Ruta donde tenemos guardado el fichero csv y el nombre del fichero
ruta_fichero <- "D:/Laura - UTAD/Tercero/Segundo cuatrimestre/Búsqueda y Análisis de la Información/Tareas/Tarea final/"

fichero <- "Mass Shootings Dataset Ver 5.csv"

# Leemos el archivo CSV 
  # 'paste0' se usa para construir la ruta completa dell archivo
  # 'header = TRUE' indica que la primera fila contiene los nombres de las columnas
  # 'sep = ","' especifica que el separador de campos en el archivo CSV es una coma
  
datos <- read.csv(file = paste0(ruta_fichero, fichero), header = TRUE, sep = ",")
# Verificamos la estructura y el resumen de los datos
summary(datos)
##        S.           Title             Location             Date          
##  Min.   :  1.0   Length:323         Length:323         Length:323        
##  1st Qu.: 81.5   Class :character   Class :character   Class :character  
##  Median :162.0   Mode  :character   Mode  :character   Mode  :character  
##  Mean   :162.0                                                           
##  3rd Qu.:242.5                                                           
##  Max.   :323.0                                                           
##                                                                          
##  Incident.Area      Open.Close.Location    Target             Cause          
##  Length:323         Length:323          Length:323         Length:323        
##  Class :character   Class :character    Class :character   Class :character  
##  Mode  :character   Mode  :character    Mode  :character   Mode  :character  
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##    Summary            Fatalities        Injured        Total.victims   
##  Length:323         Min.   : 0.000   Min.   :  0.000   Min.   :  3.00  
##  Class :character   1st Qu.: 1.000   1st Qu.:  1.000   1st Qu.:  4.00  
##  Mode  :character   Median : 3.000   Median :  3.000   Median :  5.00  
##                     Mean   : 4.437   Mean   :  6.176   Mean   : 10.26  
##                     3rd Qu.: 5.500   3rd Qu.:  5.000   3rd Qu.:  9.00  
##                     Max.   :59.000   Max.   :527.000   Max.   :585.00  
##                                                                        
##  Policeman.Killed     Age            Employeed..Y.N.  Employed.at       
##  Min.   :0.0000   Length:323         Min.   :0.0000   Length:323        
##  1st Qu.:0.0000   Class :character   1st Qu.:0.0000   Class :character  
##  Median :0.0000   Mode  :character   Median :1.0000   Mode  :character  
##  Mean   :0.1293                      Mean   :0.6269                     
##  3rd Qu.:0.0000                      3rd Qu.:1.0000                     
##  Max.   :5.0000                      Max.   :1.0000                     
##  NA's   :6                           NA's   :256                        
##  Mental.Health.Issues     Race              Gender             Latitude    
##  Length:323           Length:323         Length:323         Min.   :21.33  
##  Class :character     Class :character   Class :character   1st Qu.:33.57  
##  Mode  :character     Mode  :character   Mode  :character   Median :36.44  
##                                                             Mean   :37.23  
##                                                             3rd Qu.:41.48  
##                                                             Max.   :60.79  
##                                                             NA's   :20     
##    Longitude      
##  Min.   :-161.79  
##  1st Qu.:-110.21  
##  Median : -88.12  
##  Mean   : -94.43  
##  3rd Qu.: -81.70  
##  Max.   : -69.71  
##  NA's   :20
# Imprimimos el número total de filas
print(paste("Numero total de filas:", nrow(datos)))
## [1] "Numero total de filas: 323"
# Mostramos los nombres de las columnas del data.frame 
names(datos)
##  [1] "S."                   "Title"                "Location"            
##  [4] "Date"                 "Incident.Area"        "Open.Close.Location" 
##  [7] "Target"               "Cause"                "Summary"             
## [10] "Fatalities"           "Injured"              "Total.victims"       
## [13] "Policeman.Killed"     "Age"                  "Employeed..Y.N."     
## [16] "Employed.at"          "Mental.Health.Issues" "Race"                
## [19] "Gender"               "Latitude"             "Longitude"
# Verificamos si todos los valores en 'S.' son únicos
print(paste("Los identificadores son únicos:", all(unique(datos$S.) == datos$S.)))
## [1] "Los identificadores son únicos: TRUE"

El conjunto de datos contiene un total de 323 filas. Las columnas disponibles en el data frame son:

  1. S: Identificador único para cada registro en el conjunto de datos.

  2. Title: Título o breve descripción del incidente.

  3. Location: Ubicación geográfica donde ocurrió el incidente.

  4. Date: Fecha en la que tuvo lugar el incidente.

  5. Incident.Area: Área específica donde ocurrió el incidente, por ejemplo, una escuela, un cine, etc.

  6. Open.Close.Location: Indica si el incidente ocurrió en un espacio cerrado o en un espacio abierto.

  7. Target: Audiencia o grupo objetivo del ataque.

  8. Cause: La razón detrás del incidente.

  9. Summary: Resumen detallado del evento, proporcionando contexto y detalles adicionales no cubiertos en otras columnas.

  10. Fatalities: Número de personas fallecidas como resultado del incidente.

  11. Injured: Número de personas heridas en el incidente.

  12. Total.victims: Total de víctimas involucradas, que incluye tanto a los heridos como a los fallecidos.

  13. Policeman.Killed: Número de oficiales de policía que fueron asesinados durante el incidente.

  14. Age: Edad del autor o autores del tiroteo.

  15. Employeed..Y.N.: Indica si el tirador estaba empleado (Y) o no (N) en el momento del incidente.

  16. Employed.at: Nombre del empleo del tirador, si estaba empleado.

  17. Mental.Health.Issues: Indica si se conocen problemas de salud mental del tirador.

  18. Race: Raza del tirador.

  19. Gender: Género del tirador.

  20. Latitude: Coordenada de latitud del lugar del incidente.

  21. Longitude: Coordenada de longitud del lugar del incidente.

Analizamos los datos que hemos cargado:

# Creamos tablas de contingencia para combinaciones de columnas
genero_salud_mental <- table(datos$Gender, datos$Mental.Health.Issues)
raza_genero <- table(datos$Race, datos$Gender)

# Imprimimos los resultados
list(Genero_y_Salud_Mental = genero_salud_mental, Raza_y_Genero = raza_genero)
## $Genero_y_Salud_Mental
##              
##               No Unclear unknown Unknown Yes
##   Female       2       0       0       0   3
##   M            3      10       0       0   7
##   M/F          0       1       0       0   0
##   Male        85       2       1      88  96
##   Male/Female  3       0       0       1   0
##   Unknown      0       0       0      21   0
## 
## $Raza_y_Genero
##                                                      
##                                                       Female   M M/F Male
##                                                            0   2   0    0
##   Asian                                                    0   1   0    5
##   Asian American                                           0   0   0   10
##   Asian American/Some other race                           0   0   0    1
##   black                                                    0   0   0    3
##   Black                                                    0   5   0    0
##   Black American or African American                       0   0   0   76
##   Black American or African American/Unknown               0   0   0    1
##   Latino                                                   0   3   0    2
##   Native American or Alaska Native                         1   0   0    2
##   Other                                                    0   1   1    0
##   Some other race                                          0   0   0   20
##   Two or more races                                        0   0   0    2
##   Unknown                                                  0   0   0   21
##   white                                                    0   0   0   12
##   White                                                    0   8   0    1
##   White American or European American                      4   0   0  116
##   White American or European American/Some other Race      0   0   0    0
##                                                      
##                                                       Male/Female Unknown
##                                                                 0       0
##   Asian                                                         0       0
##   Asian American                                                1       0
##   Asian American/Some other race                                0       0
##   black                                                         0       0
##   Black                                                         0       0
##   Black American or African American                            0       0
##   Black American or African American/Unknown                    0       0
##   Latino                                                        0       0
##   Native American or Alaska Native                              0       0
##   Other                                                         0       0
##   Some other race                                               0       0
##   Two or more races                                             0       0
##   Unknown                                                       0      21
##   white                                                         0       0
##   White                                                         0       0
##   White American or European American                           2       0
##   White American or European American/Some other Race           1       0

Pre-proceso dataframe y limpieza del dataset

# Convertimos variables categóricas importantes a factores
datos$Title <- as.factor(datos$Title)
datos$Summary <- as.factor(datos$Summary)
datos$Location <- as.factor(datos$Location)
datos$Open.Close.Location <- as.factor(datos$Open.Close.Location)
datos$Fatalities <- as.factor(datos$Fatalities)
datos$Injured <- as.factor(datos$Injured)
datos$Total.victims <- as.factor(datos$Total.victims)
datos$Target <- as.factor(datos$Target)
datos$Age <- as.factor(datos$Age)
datos$Cause <- as.factor(datos$Cause)
datos$Mental.Health.Issues <- as.factor(datos$Mental.Health.Issues)
datos$Race <- as.factor(datos$Race)
datos$Gender <- as.factor(datos$Gender)

# Convertimos la columna de fechas a formato de fecha
datos$Date <- as.Date(datos$Date, format = "%m/%d/%Y")

# Convertimos a POSIXct (fecha y hora)
datos$Date_posix <- as.POSIXct(datos$Date, format = "%m/%d/%Y")

Limpiamos los datos de carácteres no deseados para un mejor análisis:

# Eliminamos los niveles que no tienen ningún registro: usamos droplevels
datos_limpios <- droplevels(datos)

# Limpieza básica del conjunto de datos
datos_limpios <- datos_limpios %>%
  # Aseguramos que los tipos de datos numéricos sean correctos
  # Eliminamos todo excepto números y puntos
   mutate(
    Fatalities = as.numeric(gsub("[^0-9.]", "", Fatalities)),  
    Injured = as.numeric(gsub("[^0-9.]", "", Injured)),
    Total.victims = as.numeric(gsub("[^0-9.]", "", Total.victims)),
    Policeman.Killed = as.numeric(gsub("[^0-9.]", "", Policeman.Killed)),
    Age = as.numeric(gsub("[^0-9.]", "", Age))
  ) %>%
  # Hacemos algunos datos mas legibles
  mutate(
    Mental.Health.Issues = case_when(
      Mental.Health.Issues %in% c("Yes", "Y") ~ "Yes",
      Mental.Health.Issues %in% c("No", "N") ~ "No",
      TRUE ~ as.character(Mental.Health.Issues)  # Mantenemos original si no coincide
    ),
    Gender = case_when(
      Gender %in% c("Male", "M") ~ "Male",
      Gender %in% c("Female", "F") ~ "Female",
      TRUE ~ as.character(Gender)  # Mantenemos original si no coincide
    )
  )

# Eliminamos caracteres no ASCII de la columna Summary
datos_limpios$Summary <- iconv(datos_limpios$Summary, to = "ASCII", sub = " ")

# Pasamos el resumen (summary) a minúsculas
datos_limpios$Summary <- tolower(datos_limpios$Summary)

Separamos la columna de Location en State y City para un mejor análisis:

# Convertimos la columna Location a carácter si no lo es ya
datos_limpios$Location <- as.character(datos_limpios$Location)

# Separación de 'Location' en 'City' y 'State'
datos_limpios <- datos_limpios %>%
  mutate(State = sapply(Location, function(parts) {
    temp <- strsplit(parts, split = ",")
    sapply(temp, function(new) {
        if(length(new) > 1) {
            str_trim(new[2])  # Eliminamos espacios en blanco
        } else {
            NA  # Si no hay estado, ponemos NA
        }
    })
  }),
  City = sapply(Location, function(parts) {
    temp <- strsplit(parts, split = ",")
    sapply(temp, function(new) {
        if(length(new) > 1) {
            str_trim(new[1])  # Eliminamos espacios en blanco
        } else {
             NA  # Si no hay ciudad, ponemos NA
        }
    })
  }))

# Guardamos el conjunto de datos limpio para análisis posterior
save(datos_limpios, file = "datos_limpios.rda")

Filtración de los datos según el tema de análisis

Hacemos un pequeño filtrado para estudiar los datos que hemos limpiado del dataset.

Filtrado por grupo de edad

Realizamos un análisis de los tiroteos agrupando a los tiradores por grupos de edad:

# Creamos un factor de grupo de edad
datos_limpios_filtracion <- datos_limpios %>%
  mutate(Age_Group = case_when(
    Age < 25 ~ "Menores de 25",
    Age >= 25 & Age <= 40 ~ "25 a 40",
    Age > 40 ~ "Mayores de 40"
  ))
# Vemos la distribución del número de víctimas por grupo de edad
summary_stats <- datos_limpios_filtracion %>%
  group_by(Age_Group) %>%
  summarise(
    Count = n(),
    Average_Victims = mean(Total.victims, na.rm = TRUE),
    Median_Victims = median(Total.victims, na.rm = TRUE),
    Max_Victims = max(Total.victims, na.rm = TRUE)
  )
  
# Vemos los resultados
print(summary_stats)
## # A tibble: 4 × 5
##   Age_Group     Count Average_Victims Median_Victims Max_Victims
##   <chr>         <int>           <dbl>          <dbl>       <dbl>
## 1 25 a 40          63           11.9               7         102
## 2 Mayores de 40    55           20.2               7         585
## 3 Menores de 25    61           10.9               6          82
## 4 <NA>            144            5.52              4          35
  • Mayores de 40 años: Este grupo tiene el promedio más alto de víctimas (20.20) y el mayor número de víctimas (585), sugiriendo que los tiradores de este grupo pueden estar involucrados en más incidentes.

  • 25 a 40 años: Aunque tienen un promedio de víctimas menor (11.86) comparado con los mayores de 40 años, este grupo también incluye incidentes con un alto número de víctimas (máximo de 102).

  • Menores de 25 años: Este grupo tiene el promedio más bajo de víctimas (10.85) entre los grupos con edades disponibles, pero aún incluyen incidentes con un número significativo de víctimas (máximo de 82).

  • NA (No Disponible): Los casos sin información de edad tienen un promedio de víctimas significativamente más bajo (5.52).

Filtrado por menciones de salud mental

Realizamos un análisis de los tiroteos agrupando a los tiradores por grupos dependiendo de si tenían o no problemas de salud mental:

# Análisis de datos filtrados
mental_health_analysis <- datos_limpios %>%
  group_by(Mental.Health.Issues) %>%
  summarise(
    Total_Incidents = n(),
    Average_Fatalities = mean(Fatalities, na.rm = TRUE),
    Average_Injured = mean(Injured, na.rm = TRUE),
    Total_Victims_Average = mean(Total.victims, na.rm = TRUE)
  )

# Vemos los resultados
print(mental_health_analysis)
## # A tibble: 5 × 5
##   Mental.Health.Issues Total_Incidents Average_Fatalities Average_Injured
##   <chr>                          <int>              <dbl>           <dbl>
## 1 No                                93               3.90            3.41
## 2 Unclear                           13              12.8            50   
## 3 Unknown                          110               2.54            3.28
## 4 Yes                              106               5.81            6.28
## 5 unknown                            1               9               1   
## # ℹ 1 more variable: Total_Victims_Average <dbl>
  • Problemas de Salud Mental No Claros (Unclear) tienen un alto promedio de víctimas, lo que sugiere que estos incidentes pueden ser más mortales.

  • Con Problemas de Salud Mental (Yes) también muestran un alto promedio de víctimas, apoyando la idea de que los problemas de salud mental pueden estar asociados con la gravedad de los incidentes.

  • Problemas de Salud Mental Desconocidos (Unknown) presentan el mayor número de incidentes, pero con menor gravedad en términos de víctimas.

Análisis de los estados y ciudades con más tiroteos

Realizamos un estudio de las ciudades y sus estados con más tiroteos:

# Contamos los tiroteos por ciudad y estado, eliminando los NA, y ordenando de mayor a menor
shooting_counts <- datos_limpios %>%
  drop_na(City, State) %>%
  group_by(City, State) %>%
  summarise(Total_Shootings = n(), .groups = 'drop') %>%
  arrange(desc(Total_Shootings))

# Verificamos las primeras filas del conteo
head(shooting_counts)

Podemos ver que la ciudad con más tiroteos es Seattle (Washington), seguida de Killeen (Texas) y Pheonix (Arizona), según los datos recogidos en este dataset.

Análisis del tiroteo con mayor número de víctimas

# Encontramos el tiroteo con más víctimas 
tiroteo_max_victimas <- datos_limpios %>%   
  filter(Total.victims == max(Total.victims, na.rm = TRUE)) %>%   
  select(Title, Date, Location, Incident.Area, Open.Close.Location, Target, Cause, Summary, Fatalities, Injured, Total.victims, Policeman.Killed, Mental.Health.Issues, Race, Gender) %>%   
  droplevels()  

# Mostramos el resultado 
print(tiroteo_max_victimas)
##                           Title       Date      Location
## 1 Las Vegas Strip mass shooting 2017-10-01 Las Vegas, NV
##                                 Incident.Area Open.Close.Location Target
## 1 Las Vegas Strip Concert outside Mandala Bay                Open random
##     Cause
## 1 unknown
##                                                                                                                                                                                                     Summary
## 1 stephen craig paddock, opened fire from the 32nd floor of manadalay bay hotel at last vegas concert goers for no obvious reason. he shot himself and died on arrival of law enforcement agents. he was 64
##   Fatalities Injured Total.victims Policeman.Killed Mental.Health.Issues  Race
## 1         59     527           585                1              Unclear White
##   Gender
## 1   Male

Stephen Paddock, un contador retirado y jugador de video póker de altas apuestas, fue el responsable del tiroteo masivo en Las Vegas en 2017, considerado el más mortal en la historia moderna de EE. UU. Desde su habitación en el Mandalay Bay Hotel, Paddock disparó a los asistentes al festival Route 91 Harvest, matando a 60 personas e hiriendo a 867. A pesar de las investigaciones extensas, los motivos de Paddock siguen siendo en gran parte inconclusos, aunque se especula que pudo haber estado descontento con el trato recibido en los casinos como gran apostador. Paddock se había preparado meticulosamente, acumulando un arsenal significativo y modificando sus armas para aumentar la cadencia de fuego. No se encontraron sustancias psicoactivas en su sistema al momento del tiroteo.

Podemos ver que el número de víctimas mortales y sobre todo el de heridos, es mayor que el que aparece en la base de datos. Esto puede deberse a que muchos datos sobre las personas heridas salieron a la luz más tarde de la fecha en la que se generó este dataset. Además, cabe destacar que, como se explica en el contexto del tiroteo, no se supo la causa exacta de este ataque, por lo que en Cause aparece Unknown.

Análisis del número de tiroteos por año

Vamos a analizar el número de tiroteos en cada año, de forma que podamos analizar más tarde aquellos años en los que hubo más tiroteos y en los que hubo menos, con la finalidad de sacar conclusiones:

# Extraemos el año de la fecha
datos_limpios$Year <- format(datos_limpios$Date, "%Y")

# Agrupamos por año y contamos tiroteos
yearly_shootings <- datos_limpios %>%
  group_by(Year) %>%
  summarise(Shootings = n(), .groups = 'drop')

# Vemos los datos
print(yearly_shootings)
## # A tibble: 42 × 2
##    Year  Shootings
##    <chr>     <int>
##  1 1966          2
##  2 1971          1
##  3 1972          1
##  4 1974          2
##  5 1976          2
##  6 1979          2
##  7 1982          2
##  8 1983          2
##  9 1984          3
## 10 1985          2
## # ℹ 32 more rows
# Guardamos en una variable los años y en otra los tiroteos
shootings_by_year <- yearly_shootings$Shootings
years <- yearly_shootings$Year

# Creamos el gráfico de barras
barplot(shootings_by_year,
        names.arg = years,  # Etiquetas del eje x (años)
        xlab = "Año",      # Etiqueta del eje x
        ylab = "Número de tiroteos",  # Etiqueta del eje y
        main = "Número de tiroteos por año",  # Título del gráfico
        col = "steelblue",  # Color de las barras
        las = 2,            # Orientación de las etiquetas del eje x (vertical)
        cex.names = 0.6,    # Tamaño de las etiquetas del eje x
        cex.axis = 0.7,     # Tamaño del texto del eje
        ylim = c(0, max(shootings_by_year) * 1.2) # Ajustar el límite del eje y
)

Este gráfico muestra el número de tiroteos por año en Estados Unidos desde 1966 hasta 2017. La tendencia general que se puede observar es que, a lo largo de las décadas, el número de tiroteos ha aumentado significativamente.

En las primeras décadas, de 1966 a principios de los 2000, la cantidad de tiroteos anuales es relativamente baja, normalmente debajo de 20 incidentes por año. A partir de mediados de los años 2000, se observa un incremento notable en la frecuencia de estos eventos, terminando en un pico muy pronunciado en los años 2015 y 2016. Estos dos años muestran un aumento significativo en la cantidad de tiroteos, con cifras que se acercan a los 80 tiroteos anuales.

Este aumento puede estar influenciado por varios factores, como cambios en las leyes de control de armas, factores socioeconómicos, y cambios culturales y políticos.

Análisis de la distribución de víctimas en 2015-2016

Como ya hemos visto, estos dos fueron los años con más tiroteos, por lo que vamos a hacer un estudio más detenido de ellos.

# Calculamos la distribución de víctimas
ggplot(datos_limpios %>% filter(Year %in% c("2015", "2016")), aes(x = as.numeric(Total.victims))) +
  geom_histogram(bins = 30, fill = "blue", color = "black") +
  labs(title = "Distribución de Total de Víctimas en 2015-2016", x = "Total de Víctimas", y = "Frecuencia") +
  theme_minimal()

Podemos ver que, en general, el número de víctimas está entre 1 y 10. Sin embargo, llama la atención que hay uno alrededor de 100 víctimas. Vamos a analizarlo:

# Filtramos los datos para los años 2015 y 2016
datos_2015_2016 <- datos_limpios %>%
  filter(Year %in% c("2015", "2016"))

# Encontramos el tiroteo con el mayor número de víctimas
tiroteo_max_victimas_2015_2016 <- datos_2015_2016 %>%
  filter(Total.victims == max(Total.victims, na.rm = TRUE))

# Vemos los detalles del tiroteo 
print(tiroteo_max_victimas_2015_2016)
##   S.                      Title         Location       Date Incident.Area
## 1 14 Orlando nightclub massacre Orlando, Florida 2016-06-12  at nightclub
##   Open.Close.Location Target Cause
## 1               Close random      
##                                                                                                                                                                           Summary
## 1 omar mateen, 29, attacked the pulse nighclub in orlando in the early morning hours of june 12. he was killed by law enforcement who raided the club after a prolonged standoff.
##   Fatalities Injured Total.victims Policeman.Killed Age Employeed..Y.N.
## 1         49      53           102                0  29              NA
##   Employed.at Mental.Health.Issues  Race Gender Latitude Longitude Date_posix
## 1                          Unclear Other   Male       NA        NA 2016-06-12
##     State    City Year
## 1 Florida Orlando 2016

Omar Mateen, de 29 años, llevó a cabo un ataque en el club nocturno Pulse en Orlando en las primeras horas del 12 de junio de 2016. Durante este suceso, Mateen disparó contra los asistentes al club, resultando en la muerte de 49 personas y dejando heridas a 53 más. La situación se prolongó durante varias horas debido a un enfrentamiento con la policía, que finalmente irrumpió en el club y terminó con la vida de Mateen. Este ataque es uno de los tiroteos masivos más mortíferos en la historia reciente de los Estados Unidos.

Análisis de los tiroteos por mes en 2015

# Filtramos los datos para el año 2015
datos_2015 <- datos_limpios %>%
  filter(Year == "2015")

# Extraemos el mes de la fecha
datos_2015$Month <- format(datos_2015$Date, "%m")

# Agrupamos por mes y contamos el número de tiroteos
monthly_shootings_2015 <- datos_2015 %>%
  group_by(Month) %>%
  summarise(Shootings = n(), .groups = 'drop')

# Vemos los datos
print(monthly_shootings_2015)
## # A tibble: 12 × 2
##    Month Shootings
##    <chr>     <int>
##  1 01            5
##  2 02           12
##  3 03            6
##  4 04            6
##  5 05            7
##  6 06            5
##  7 07            4
##  8 08            4
##  9 09            3
## 10 10            5
## 11 11            7
## 12 12            3
# Guardamos en una variable los meses y en otra los tiroteos
shootings_by_month_2015 <- monthly_shootings_2015$Shootings
months_2015 <- monthly_shootings_2015$Month

# Creamos el gráfico de barras para los meses de 2015
barplot(shootings_by_month_2015,
        names.arg = months_2015,  
        xlab = "Mes",      
        ylab = "Número de tiroteos",  
        main = "Número de tiroteos por mes en 2015",  
        col = "darkred",  
        las = 2,            
        cex.names = 0.6,    
        cex.axis = 0.7,     
        ylim = c(0, max(shootings_by_month_2015) * 1.2) 
)

El gráfico muestra el número de tiroteos que ocurrieron en cada mes del año 2015. Observamos que febrero tiene el mayor número de tiroteos con 12 incidentes, seguido de noviembre y mayo con 7. Otros meses, como marzo y abril, también presentan un número relativamente alto de tiroteos en comparación con otros meses.

# Agrupamos por mes y calculamos estadísticas descriptivas
monthly_stats_2015 <- datos_2015 %>%
  group_by(Month) %>%
  summarise(
    Shootings = n(),
    Mean_Fatalities = mean(Fatalities, na.rm = TRUE),
    Median_Fatalities = median(Fatalities, na.rm = TRUE),
    Total_Fatalities = sum(Fatalities, na.rm = TRUE),
    .groups = 'drop'
  )

# Vemos las estadísticas descriptivas
print(monthly_stats_2015)
## # A tibble: 12 × 5
##    Month Shootings Mean_Fatalities Median_Fatalities Total_Fatalities
##    <chr>     <int>           <dbl>             <dbl>            <dbl>
##  1 01            5            3                  3                 15
##  2 02           12            3.17               3                 38
##  3 03            6            2                  2                 12
##  4 04            6            1.5                1                  9
##  5 05            7            2.71               3                 19
##  6 06            5            3.4                3                 17
##  7 07            4            3.5                3.5               14
##  8 08            4            4.25               3                 17
##  9 09            3            3.67               5                 11
## 10 10            5            4                  3                 20
## 11 11            7            3.14               4                 22
## 12 12            3           10.7               14                 32
  1. Frecuencia de Tiroteos por Mes:

    • Febrero tuvo el mayor número de tiroteos (12), seguido de noviembre (7) y mayo (7).

    • Septiembre y diciembre registraron los menos tiroteos (3 cada uno).

  2. Fatalidades por Mes:

    • Diciembre presenta la mayor media de fatalidades (10.7) y la mediana más alta (14), sugiriendo que ocurrieron incidentes con un número muy alto de víctimas.

    • Agosto también muestra una alta media de fatalidades (4.25), a pesar del menor número de tiroteos (4).

  3. Total de Fatalidades:

    • Diciembre y febrero sobresalen con los totales más altos de fatalidades (32 y 38, respectivamente).

    • Otros meses con altos totales de fatalidades incluyen noviembre (22) y octubre (20).

  4. Mediana de Fatalidades:

    • La mediana de fatalidades por tiroteo fue generalmente consistente, con la mayoría de los meses mostrando una mediana de alrededor de 3 a 4 fatalidades. Esto indica que, aunque el número de tiroteos varía, la gravedad en términos de víctimas por incidente se mantiene relativamente constante en varios meses.
  5. Conclusiones

    Existe una notable variabilidad en el número de tiroteos y en las fatalidades asociadas a lo largo del año. Febrero destaca por su alta frecuencia de tiroteos, mientras que diciembre muestra una gravedad en términos de número de víctimas por incidente.

Haciendo un view de datos_2015, podemos ver que diciembre, a pesar de solo tener registrados 3 tiroteos, dos de ellos tienen un alto número de fatalidades, heridos y víctimas, lo que justifica la elevada media de fatalidades que hemos obtenido.

Viendo algunos cógidos relacionados con el tema, el notebook Toby’s Visualizing Social Injustice me dio la idea de visualizar estos datos con un mapa interactivo.

En este código, usan la librería folium, pero yo he decidido usar otra llamada leaflet.

Podemos realizar este estudio porque el dataset tiene dos columnas relacionadas con la posición geográfica, que son Latitude y Longitude.

# Instalams y cargamos la librería 'leaflet' para visualización en mapas
# install.packages("leaflet")
library(leaflet)

# Filtramos los datos de 2015 con información geográfica
datos_2015_geo <- datos_2015 %>%
  filter(!is.na(Latitude) & !is.na(Longitude))

# Creamos una paleta de colores más oscura para los meses
pal <- colorFactor(palette = brewer.pal(8, "Dark2"), domain = datos_2015_geo$Date)

# Creamos un mapa interactivo
map <- leaflet(datos_2015_geo) %>%
  addTiles() %>%
  addCircles(
    lat = ~Latitude, lng = ~Longitude, weight = 1,
    radius = ~Fatalities * 10000, 
    popup = ~paste("Fecha:", Date, "<br>", "Fatalities:", Fatalities),
    color = ~pal(Date), fillOpacity = 0.7
  )

# Mostramos el mapa
map

El mapa interactivo muestra la distribución geográfica de los tiroteos en Estados Unidos durante 2015, destacando una alta concentración en áreas como California y Oregon. Los círculos varían en tamaño según el número de fatalidades, con incidentes particularmente graves en California, que presenta el círculo más grande.

# Filtramos los datos para California/Los Ángeles utilizando términos generales
datos_la <- datos_2015 %>% filter(grepl("Los Angeles|LA|California", Location, ignore.case = TRUE))

# Vemos los datos filtrados
print(datos_la)
##     S.                                           Title
## 1   81                    San Bernardino mass shooting
## 2   82                      San Bernardino, California
## 3   89                                  Oakland, Maine
## 4   92 Northern Arizona University at Flagstaff Campus
## 5   96                            Platte, South Dakota
## 6  102                     Grand 16 Theatre, Louisiana
## 7  111                                       Cleveland
## 8  124                                           Tulsa
## 9  128                                      Cottonwood
## 10 131                    Birmingham's Washington Park
## 11 134                                    Clarkesville
## 12 139                                    Douglasville
## 13 142                                        LaGrange
##                      Location       Date               Incident.Area
## 1  San Bernardino, California 2015-12-02             Christmas Party
## 2  San Bernardino, California 2015-12-02             conference room
## 3              Oakland, Maine 2015-11-04                        Home
## 4          Flagstaff, Arizona 2015-10-09 Northern Arizona University
## 5        Platte, South Dakota 2015-09-17                        Home
## 6        Lafayette, Louisiana 2015-07-23               Movie Theatre
## 7             Cleveland, Ohio 2015-05-31                        Home
## 8             Tulsa, Oklahoma 2015-03-30                        Home
## 9         Cottonwood, Alabama 2015-03-15                        Home
## 10        Birmingham, Alabama 2015-02-27                      street
## 11      Clarkesville, Georgia 2015-02-22                        Home
## 12      Douglasville, Georgia 2015-02-07                        Home
## 13          LaGrange, Georgia 2015-01-28                        Home
##    Open.Close.Location        Target            Cause
## 1                Close        random        terrorism
## 2                Close  party guests        terrorism
## 3                Close        Family domestic dispute
## 4                 Open      Students            anger
## 5                Close        Family                 
## 6                Close        random                 
## 7                Close Ex-girlfriend           psycho
## 8                Close        Family                 
## 9                Close        Family                 
## 10                Open       Friends           psycho
## 11               Close       Ex-Wife                 
## 12               Close        Family           psycho
## 13               Close        Family           psycho
##                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    Summary
## 1                                                                                                                                                                                                                                                                                                           syed rizwan farook left a christmas party held at inland regional center, later returning with tashfeen malik and the two opened fire, killing 14 and wounding 21, ten critically. the two were later killed by police as they fled in an suv.
## 2          on wednesday of december 2, 2015, two gunmen entered a conference room on the inland regional center property, killing 14 people, and injuring 21 people. the facility was rented by the san bernardino county department of public health for a holiday party. the shooter was at the party but left abruptly before the shooting. he seemed angry, witnesses told police. he returned and along with his wife, he went into the building and began firing. the man was an inspector with the county health department that hosted the party. 
## 3  a gunman shot his girlfriend,  his girlfriend's sister and her boyfriend to death in a central maine apartment building, leaving a young girl unharmed on the first floor before turning the weapon on himself. they all shared the brown clapboard home, which was divided into two apartments. the gunman and his girlfriend lived in the upstairs room of the apartment while the two other victims (his girlfriend's sister and her boyfriend) lived in the downstairs with their 3 year old daughter. all three victims were found on first floor.
## 4                                                                                                                                                                                                                                                              on friday of october 9, 2015, one person is dead and three are wounded after an early morning shooting on northern arizona university's campus. an overnight confrontation between two groups of students escalated into gunfire. the shooter is a freshman of northern arizona university.
## 5                                                                                                                                                                                                                                                                                                                                        the six members of a south dakota family found dead in the ruins of their burned home were fatally shot, with one death believed to be a suicide. all six members of the shooter's family died of shotgun wounds.
## 6                                                                                                                                                                                                                                                                                                           on july 23, 2015, a shooting occurred at the grand 16 theater movie theater in lafayette, louisiana. the shooter opened fire during a showing of the film trainwreck, killing two people and injuring nine others before he committed suicide.
## 7                                                                                                                                                                                                                                                                                                                                                                            on may 31, 2015, the shooter entered his ex-girlfriend's home and found his ex-girlfriend and her grandfather upstairs, shooting and killing both. he then took his own life.
## 8                                                                                                           on march 30, 2015, police discovered four bodies of a family, including two young boys, inside a home in tulsa, oklahoma on wednesday in what appeared to be a murder-suicide. officers discovered the bodies wednesday afternoon as they conducted a welfare check requested by the mother's employer who was concerned after the woman failed to show up for work or call in sick for two days. a handgun was found near the father's body. 
## 9                                                                                                                                                                                                                                                                                                                                                                                              on sunday night of march 15, the man broke into a house and killed two people including his wife and shot another person before turning the gun on himself.
## 10                                                                                                                                                                                                                                                     on february 27, 2015, the online facebook brawl between two groups of friends spilled out into the streets with a planned videotaped fight.  after the melee ensued, two teenage males pulled out guns and started shooting at the girls. the shots killed 14-year-old girl and wounded two others.
## 11                                                                                                                                                                                                                                                                                                                          on february 22, 2015, a former police officer fatally shot his ex-wife and her friend, then wounded a local sheriff and a deputy at his ex-wife's home on sunday before he was killed by the return fire from law enforcement.
## 12                                                                                                                                                                                                                                                                                                                                                         on february 7, 2015, a man entered the house of his ex-wife and shot her, two of their children, her boyfriend, and wounded two of her children. he then went onto the street and shot himself.
## 13                                                                                                                                                                                                                                                                                                                               on january 28, 2015, in a georgia home, a man shot three family members and one friend, and strangled his daughter in law. all five died. he left town with the family's dog, and police arrested him several days later.
##    Fatalities Injured Total.victims Policeman.Killed Age Employeed..Y.N.
## 1          14      21            35                0  NA              NA
## 2          16      21            35                0  NA               1
## 3           4       0             3                0  NA              NA
## 4           1       3             4                0  NA              NA
## 5           6       0             5                0  NA              NA
## 6           3       9            11                0  NA              NA
## 7           3       1             3                0  NA              NA
## 8           4       0             3                0  NA              NA
## 9           3       1             3                0  NA              NA
## 10          1       2             3                0  NA              NA
## 11          3       2             4                0  NA              NA
## 12          5       2             6                0  NA              NA
## 13          5       0             5                0  NA              NA
##                 Employed.at Mental.Health.Issues
## 1                                        Unclear
## 2  county health department              Unknown
## 3                                        Unknown
## 4                                        Unknown
## 5                                        Unknown
## 6                                            Yes
## 7                                        Unknown
## 8                                        Unknown
## 9                                             No
## 10                                            No
## 11                                            No
## 12                                            No
## 13                                            No
##                                   Race      Gender Latitude  Longitude
## 1                                Other         M/F       NA         NA
## 2                       Asian American Male/Female 34.13973 -117.29424
## 3                              Unknown        Male 44.54995  -69.70782
## 4  White American or European American        Male 35.17257 -111.65854
## 5  White American or European American        Male 43.38683  -98.84357
## 6  White American or European American        Male 30.21234  -92.03165
## 7                              Unknown        Male 41.47658  -81.68052
## 8                       Asian American        Male 36.13543  -95.91316
## 9  White American or European American        Male 31.05497  -85.30100
## 10  Black American or African American        Male 33.52829  -86.79550
## 11 White American or European American        Male 34.61026  -83.52918
## 12  Black American or African American        Male 33.71059  -84.71564
## 13 White American or European American        Male 33.03607  -85.02871
##    Date_posix        State           City Year Month
## 1  2015-12-02   California San Bernardino 2015    12
## 2  2015-12-02   California San Bernardino 2015    12
## 3  2015-11-04        Maine        Oakland 2015    11
## 4  2015-10-09      Arizona      Flagstaff 2015    10
## 5  2015-09-17 South Dakota         Platte 2015    09
## 6  2015-07-23    Louisiana      Lafayette 2015    07
## 7  2015-05-31         Ohio      Cleveland 2015    05
## 8  2015-03-30     Oklahoma          Tulsa 2015    03
## 9  2015-03-15      Alabama     Cottonwood 2015    03
## 10 2015-02-27      Alabama     Birmingham 2015    02
## 11 2015-02-22      Georgia   Clarkesville 2015    02
## 12 2015-02-07      Georgia   Douglasville 2015    02
## 13 2015-01-28      Georgia       LaGrange 2015    01

Analizamos el círculo más grande, que fue un tiroteo en California el 2 de diciembre de 2015:

# Filtramos los datos para la fecha específica
tiroteo_2015_12_02 <- datos_la %>% filter(Date == as.Date("2015-12-02"))

# Vemos los datos filtrados
print(tiroteo_2015_12_02)
##   S.                        Title                   Location       Date
## 1 81 San Bernardino mass shooting San Bernardino, California 2015-12-02
## 2 82   San Bernardino, California San Bernardino, California 2015-12-02
##     Incident.Area Open.Close.Location       Target     Cause
## 1 Christmas Party               Close       random terrorism
## 2 conference room               Close party guests terrorism
##                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           Summary
## 1                                                                                                                                                                                                                                                                                                  syed rizwan farook left a christmas party held at inland regional center, later returning with tashfeen malik and the two opened fire, killing 14 and wounding 21, ten critically. the two were later killed by police as they fled in an suv.
## 2 on wednesday of december 2, 2015, two gunmen entered a conference room on the inland regional center property, killing 14 people, and injuring 21 people. the facility was rented by the san bernardino county department of public health for a holiday party. the shooter was at the party but left abruptly before the shooting. he seemed angry, witnesses told police. he returned and along with his wife, he went into the building and began firing. the man was an inspector with the county health department that hosted the party. 
##   Fatalities Injured Total.victims Policeman.Killed Age Employeed..Y.N.
## 1         14      21            35                0  NA              NA
## 2         16      21            35                0  NA               1
##                Employed.at Mental.Health.Issues           Race      Gender
## 1                                       Unclear          Other         M/F
## 2 county health department              Unknown Asian American Male/Female
##   Latitude Longitude Date_posix      State           City Year Month
## 1       NA        NA 2015-12-02 California San Bernardino 2015    12
## 2 34.13973 -117.2942 2015-12-02 California San Bernardino 2015    12

El 2 de diciembre de 2015, hubo un tiroteo en San Bernardino, California. En este ataque terrorista, Syed Rizwan Farook y Tashfeen Malik llevaron a cabo un tiroteo masivo en el Inland Regional Center durante un evento del Departamento de Salud Pública del Condado de San Bernardino. El ataque resultó en 14 personas muertas y 21 heridas. Los atacantes, que estaban inspirados por ideologías extremistas islámicas, huyeron del lugar y fueron abatidos por la policía en un enfrentamiento posterior ese mismo día.

Me ha parecido interesante que en el dataset, este tiroteo está duplicado, pero uno tiene más datos que el otro, lo que nos lleva a pensar que al actualizar los datos en la nueva versión, añadieron nuevos datos pero no borraron la entrada anterior.

Análisis de los tiroteos por mes en 2016

# Filtramos los datos para el año 2016
datos_2016 <- datos_limpios %>%
  filter(Year == "2016")

# Extraemos el mes de la fecha
datos_2016$Month <- format(datos_2016$Date, "%m")

# Agrupamos por mes y contamos el número de tiroteos
monthly_shootings_2016 <- datos_2016 %>%
  group_by(Month) %>%
  summarise(Shootings = n(), .groups = 'drop')

# Vemos los datos
print(monthly_shootings_2016)
## # A tibble: 7 × 2
##   Month Shootings
##   <chr>     <int>
## 1 01           11
## 2 02           21
## 3 03           21
## 4 04           12
## 5 06            1
## 6 07            2
## 7 09            1
# Guardamos en una variable los meses y en otra los tiroteos
shootings_by_month_2016 <- monthly_shootings_2016$Shootings
months_2016 <- monthly_shootings_2016$Month

# Creamos el gráfico de barras para los meses de 2016
barplot(shootings_by_month_2016,
        names.arg = months_2016,  
        xlab = "Mes",      
        ylab = "Número de tiroteos",  
        main = "Número de tiroteos por mes en 2016",  
        col = "purple4",  # Color para 2016 (dark purple)
        las = 2,            
        cex.names = 0.6,    
        cex.axis = 0.7,     
        ylim = c(0, max(shootings_by_month_2016) * 1.2) 
)

El análisis del gráfico de tiroteos por mes en 2016 revela algunas conclusiones importantes en la distribución temporal de los incidentes. La alta actividad en febrero y marzo seguida de una disminución sostenida sugiere que en estos dos meses ocurrieron muchos más tiroteos de lo normal, con una cifra de unos 22 tiroteos en ambos meses.

# Agrupamos por mes y calculamos estadísticas descriptivas
monthly_stats_2016 <- datos_2016 %>%
  group_by(Month) %>%
  summarise(
    Shootings = n(),
    Mean_Fatalities = mean(Fatalities, na.rm = TRUE),
    Median_Fatalities = median(Fatalities, na.rm = TRUE),
    Total_Fatalities = sum(Fatalities, na.rm = TRUE),
    .groups = 'drop'
  )

# Vemos las estadísticas descriptivas
print(monthly_stats_2016)
## # A tibble: 7 × 5
##   Month Shootings Mean_Fatalities Median_Fatalities Total_Fatalities
##   <chr>     <int>           <dbl>             <dbl>            <dbl>
## 1 01           11           2.09                  2               23
## 2 02           21           2.52                  2               53
## 3 03           21           2.14                  2               45
## 4 04           12           0.833                 0               10
## 5 06            1          49                    49               49
## 6 07            2           4                     4                8
## 7 09            1           5                     5                5
  1. Frecuencia de Tiroteos por Mes:

    • Febrero y marzo tuvieron el mayor número de tiroteos (21), seguidos de abril (12).

    • Junio, julio y septiembre registraron los menos tiroteos (1 o 2 cada uno).

  2. Fatalidades por Mes:

    • Junio presenta la mayor media de fatalidades (49) y una mediana también muy alta (49), indicando un incidente con un número extremadamente alto de víctimas.
  3. Total de Fatalidades:

    • Febrero y marzo sobresalen con los totales más altos de fatalidades (53 y 45, respectivamente), además de junio, ya mencionado antes.

    • Enero y abril también tienen altos totales de fatalidades (23 y 10, respectivamente).

  4. Mediana de Fatalidades:

    • La mediana de fatalidades por tiroteo fue generalmente consistente, con la mayoría de los meses mostrando una mediana de alrededor de 2 fatalidades, excepto en abril (0) y junio (49). Esto indica que, aunque el número de tiroteos varía, la gravedad en términos de víctimas por incidente se mantiene relativamente constante en varios meses.
  5. Conclusiones:

    • Febrero y marzo destacan por su alta frecuencia de tiroteos, mientras que junio muestra una gravedad en términos de número de víctimas por incidente.

Al igual que en 2015, podemos hacer un análisis con un mapa interactivo gracias a las columnas de Latitude y Longitude del dataset.

# Instalams y cargamos la librería 'leaflet' para visualización en mapas
# install.packages("leaflet")
# library(leaflet)

# Filtramos los datos de 2016 con información geográfica
datos_2016_geo <- datos_2016 %>%
  filter(!is.na(Latitude) & !is.na(Longitude))

# Creamos una paleta de colores más oscura para los meses
pal <- colorFactor(palette = brewer.pal(8, "Dark2"), domain = datos_2016_geo$Date)

# Creamos un mapa interactivo
map <- leaflet(datos_2016_geo) %>%
  addTiles() %>%
  addCircles(
    lat = ~Latitude, lng = ~Longitude, weight = 1,
    radius = ~Fatalities * 10000, 
    popup = ~paste("Fecha:", Date, "<br>", "Fatalities:", Fatalities),
    color = ~pal(Date), fillOpacity = 0.7
  )

# Mostramos el mapa
map

Cabe destacar, que el tiroteo con mayor número de víctimas y de fatalidades, que hemos estudiado más arriba (Orlando nightclub massacre), no aparece en el mapa porque no tiene datos de latitud ni longitud.

Ley de Zipf

La ley de Zipf determina que la frecuencia de aparición de una palabra es proporcional al inverso de la posición que ocupa dicha palabra según su número de apariciones.

Cargamos y limpiamos los datos:

# Definimos una cadena de caracteres para remover ciertas entidades HTML
remove_reg <- "&amp;|&lt;|&gt;|&quot;|&apos;"

# Proceso de limpieza del dataframe 'datos_limpios'
tidy_datos_limpios <- datos_limpios %>%
  mutate(Summary = str_remove_all(Summary, remove_reg)) %>%  # Removemos entidades HTML
  mutate(Summary = str_remove_all(Summary, "[^[:alnum:][:space:]]"))  # Removemos caracteres no alfanuméricos

# Verificamos los datos limpios
print(head(tidy_datos_limpios))
##   S.                               Title               Location       Date
## 1  1          Texas church mass shooting Sutherland Springs, TX 2017-11-05
## 2  2 Walmart shooting in suburban Denver           Thornton, CO 2017-11-01
## 3  3     Edgewood businees park shooting           Edgewood, MD 2017-10-18
## 4  4       Las Vegas Strip mass shooting          Las Vegas, NV 2017-10-01
## 5  5          San Francisco UPS shooting      San Francisco, CA 2017-06-14
## 6  6   Pennsylvania supermarket shooting        Tunkhannock, PA 2017-06-07
##                                 Incident.Area Open.Close.Location    Target
## 1                                      Church               Close    random
## 2                                    Wal-Mart                Open    random
## 3                            Remodeling Store               Close coworkers
## 4 Las Vegas Strip Concert outside Mandala Bay                Open    random
## 5                                UPS facility               Close coworkers
## 6                                Weis grocery               Close coworkers
##       Cause
## 1   unknown
## 2   unknown
## 3   unknown
## 4   unknown
## 5          
## 6 terrorism
##                                                                                                                                                                                                                                                                                                                                  Summary
## 1                                                                                                                                                                                   devin patrick kelley 26 an exair force officer shot and killed 26 people and wounded 20 at a church in texas he was found dead later in his vehicle 
## 2                    scott allen ostrem 47 walked into a walmart in a suburb north of denver and fatally shot two men and a woman then left the store and drove away after an allnight manhunt ostrem who had financial problems but no serious criminal history was captured by police after being spotted near his apartment in denver
## 3 radee labeeb prince 37 fatally shot three people and wounded two others around 9am at advance granite solutions a home remodeling business where he worked near baltimore hours later he shot and wounded a sixth person at a car dealership in wilmington delaware he was apprehended that evening following a manhunt by authorities
## 4                                                                                                                                 stephen craig paddock opened fire from the 32nd floor of manadalay bay hotel at last vegas concert goers for no obvious reason he shot himself and died on arrival of law enforcement agents he was 64
## 5                                                                                                                                                          jimmy lam 38 fatally shot three coworkers and wounded two others inside a ups facility in san francisco lam killed himself as law enforcement officers responded to the scene
## 6                                                                            randy stair a 24yearold worker at weis grocery fatally shot three of his fellow employees he reportedly fired 59 rounds with a pair of shotguns before turning the gun on himself as another coworker fled the scene for help and law enforcement responded
##   Fatalities Injured Total.victims Policeman.Killed Age Employeed..Y.N.
## 1         26      20            46                0  26              NA
## 2          3       0             3                0  47              NA
## 3          3       3             6                0  37              NA
## 4         59     527           585                1  64              NA
## 5          3       2             5                0  38               1
## 6          3       0             3               NA  24               1
##             Employed.at Mental.Health.Issues  Race Gender Latitude Longitude
## 1                                         No White   Male       NA        NA
## 2                                         No White   Male       NA        NA
## 3 Advance Granite Store                   No Black   Male       NA        NA
## 4                                    Unclear White   Male 36.18127 -115.1341
## 5                                        Yes Asian   Male       NA        NA
## 6          Weis grocery              Unclear White   Male       NA        NA
##   Date_posix State               City Year
## 1 2017-11-05    TX Sutherland Springs 2017
## 2 2017-11-01    CO           Thornton 2017
## 3 2017-10-18    MD           Edgewood 2017
## 4 2017-10-01    NV          Las Vegas 2017
## 5 2017-06-14    CA      San Francisco 2017
## 6 2017-06-07    PA        Tunkhannock 2017
# Guardamos el conjunto de datos limpio para análisis posterior
save(tidy_datos_limpios, file = "tidy_datos_limpios.rda")

Tokenización y Conteo de Frecuencias:

# Desglosamos el contenido de los textos en palabras individuales (tokenización)
datos_words <- tidy_datos_limpios %>%
  unnest_tokens(word, Summary)

# Filtramos las stop words
datos_words <- datos_words %>%
  anti_join(stop_words, by = "word")

# Contamos la frecuencia de palabras por estado
datos_words <- datos_words %>%
  count(State, word, sort = TRUE)

# Verificamos los datos con las frecuencias de palabras por estado
print(head(datos_words))
##        State       word  n
## 1       <NA>     people 26
## 2 California     killed 20
## 3 California california 19
## 4       <NA>    injured 18
## 5       <NA>    shooter 17
## 6       <NA>       shot 16
# Calculamos el total de palabras por estado
total_words <- datos_words %>% 
  group_by(State) %>% 
  summarize(total = sum(n))

# Verificamos los totales de palabras por estado
print(total_words)
## # A tibble: 58 × 2
##    State       total
##    <chr>       <int>
##  1 Alabama       219
##  2 Alaska         22
##  3 Albuquerque    34
##  4 Arizona       251
##  5 Arkansas       37
##  6 CA             38
##  7 CO             26
##  8 California    834
##  9 Colorado      139
## 10 Connecticut    62
## # ℹ 48 more rows
# Unimos el total de palabras de vuelta con las frecuencias individuales de palabras
datos_words <- left_join(datos_words, total_words, by = "State")

# Verificamos los datos unidos
print(head(datos_words))
##        State       word  n total
## 1       <NA>     people 26   551
## 2 California     killed 20   834
## 3 California california 19   834
## 4       <NA>    injured 18   551
## 5       <NA>    shooter 17   551
## 6       <NA>       shot 16   551

Filtrado y Visualización de la Proporción de Palabras:

# Filtramos los datos para incluir solo los estados con más registros
top_states <- datos_words %>%
  group_by(State) %>%
  summarize(total_registros = n()) %>%
  top_n(7, wt = total_registros) %>%
  pull(State)

# Filtramos los datos para incluir solo estos estados
datos_filtrados <- datos_words %>%
  filter(State %in% top_states)

# Eliminamos filas donde 'State' es NA
datos_filtrados <- datos_filtrados %>%
  filter(!is.na(State))

# Verificamos que no haya valores NA en 'n' o 'total'
datos_filtrados <- datos_filtrados %>%
  filter(!is.na(n) & !is.na(total))

# Creamos una nueva columna para la proporción de palabras
datos_filtrados <- datos_filtrados %>%
  mutate(proporcion = n / total)

# Verificamos los datos con la nueva columna de proporción
print(head(datos_filtrados))
##        State       word  n total proporcion
## 1 California     killed 20   834 0.02398082
## 2 California california 19   834 0.02278177
## 3 California     police 15   834 0.01798561
## 4 California    shooter 15   834 0.01798561
## 5 Washington     killed 15   368 0.04076087
## 6 California       fire 14   834 0.01678657
# Ajustamos los límites del eje X para asegurarnos de que las proporciones se muestren correctamente
ggplot(datos_filtrados, aes(x = proporcion, fill = State)) +
  geom_histogram(show.legend = FALSE, bins = 30) +
  facet_wrap(~State, ncol = 2, scales = "free_y") +
  labs(x = "Proporción de palabras", y = "Frecuencia", title = "Histograma de Proporción de Palabras por Estado")

La gráfica presenta la proporción de palabras en los resúmenes de los tiroteos, dividida por estado:

  • Arizona: La mayoría de las palabras tienen una proporción muy baja, con algunas palabras con una proporción ligeramente mayor. Esto sugiere que las descripciones de los tiroteos en Arizona contienen una amplia variedad de palabras, sin que ninguna palabra domine significativamente.

  • California: Similar a Arizona, aunque hay una mayor cantidad de palabras con una proporción baja pero consistente. Esto puede indicar una mayor variedad en los incidentes descritos.

  • Florida: La distribución es similar a la de California.

  • North Carolina: Tiene una distribución más plana con una mayor cantidad de palabras con proporciones bajas, sugiriendo una mayor diversidad en los términos usados para describir los incidentes.

  • Texas: Muestra una distribución similar a los otros estados, con la mayoría de las palabras teniendo proporciones muy bajas.

  • Washington: También tiene una proporción baja de palabras, con algunas palabras destacando ligeramente más que otras.

Cálculo de TF-IDF (frecuencia de término - frecuencia de documento inversa):

# Calculamos tf-idf para identificar palabras importantes en cada estado
datos_tf_idf <- datos_filtrados %>%
  bind_tf_idf(word, State, n)

# Vemos el dataframe de tf-idf para cada palabra
print(head(datos_tf_idf))
##        State       word  n total proporcion         tf      idf     tf_idf
## 1 California     killed 20   834 0.02398082 0.02398082 0.000000 0.00000000
## 2 California california 19   834 0.02278177 0.02278177 1.791759 0.04081946
## 3 California     police 15   834 0.01798561 0.01798561 0.000000 0.00000000
## 4 California    shooter 15   834 0.01798561 0.01798561 0.000000 0.00000000
## 5 Washington     killed 15   368 0.04076087 0.04076087 0.000000 0.00000000
## 6 California       fire 14   834 0.01678657 0.01678657 0.000000 0.00000000
# Ordenamos las palabras en base a su valor tf-idf descendente
datos_tf_idf %>%
  select(-total) %>%
  arrange(desc(tf_idf)) %>%
  head(10)  # Mostramos solo las primeras 10 filas
# Graficamos las 15 palabras más altas en tf-idf por estado para identificar palabras distintivas
datos_tf_idf %>%
  group_by(State) %>%
  slice_max(tf_idf, n = 7) %>%
  ungroup() %>%
  ggplot(aes(tf_idf, fct_reorder(word, tf_idf), fill = State)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~State, ncol = 2, scales = "free") +
  labs(x = "tf-idf", y = NULL)

La gráfica presenta las palabras más distintivas en los resúmenes de tiroteos por estado, utilizando la métrica tf-idf:

  1. Arizona

    • Palabras destacadas: “arizona”, “session”, “phoenix”, “college”.

    • La palabra “arizona” es naturalmente destacada al estar en el propio nombre del estado. “Phoenix”, la capital del estado, sugiere que varios incidentes pueden haber ocurrido allí. La presencia de “college” y “session” puede indicar incidentes en instituciones educativas o durante eventos específicos en estos lugares.

  2. California

    • Palabras destacadas: “california”, “santa”, “san”, “postal”, “worker”.

    • “California” destaca como es lógico por ser el nombre del estado. “Santa” y “San” probablemente se refieren a ciudades como Santa Ana y San Francisco. “Postal” y “worker” pueden indicar tiroteos relacionados con lugares de trabajo, como oficinas postales.

  3. Florida

    • Palabras destacadas: “florida”, “party”, “orlando”, “nightclub”, “gunpoint”.

    • “Florida” resalta por ser el nombre del estado. “Orlando” sugiere incidentes en esta ciudad específica. “Party”, “nightclub” y “gunpoint” indican que varios tiroteos ocurrieron en eventos sociales y clubes nocturnos, destacando la naturaleza de estos incidentes.

  4. North Carolina

    • Palabras destacadas: “north”, “carolina”, “mobile”, “happened”, “boom”.

    • “North” y “Carolina” son palabras que representan el estado. “Mobile” podría referirse a comunicaciones móviles relacionadas con los incidentes. “Happened” y “boom” indican descripciones de los eventos en sí, con “boom” posiblemente refiriéndose a explosiones o sonidos de disparos.

  5. Texas

    • Palabras destacadas: “texas”, “hood”, “army”, “fort”, “specialist”.

    • “Texas” destaca como el nombre del estado. “Hood” podría referirse a Fort Hood, una base militar en Texas, y las palabras “army” y “fort” sugieren tiroteos relacionados con personal militar o en instalaciones militares. “Specialist” podría referirse a una especialización militar o a individuos específicos en esos incidentes.

  6. Washington

    • Palabras destacadas: “washington”, “seattle”, “coffee”, “womans”.

    • “Washington” es el nombre del estado. “Seattle” indica incidentes en esta ciudad principal. “Coffee” podría estar relacionado con tiroteos en cafeterías. “Womans” sugiere la posible implicación de mujeres ya sea como víctimas o participantes en los incidentes.

Análisis de la Ley de Zipf:

# La frecuencia de palabra es inversamente proporcional a su rango en la lista de frecuencia
freq_by_rank <- datos_filtrados %>% 
  group_by(State) %>%
  mutate(rank = row_number(),  # Asignamos un rango basado en frecuencia de palabras
         term_frequency = n / total) %>%  # Calculamos frecuencia de término
  ungroup()

# Graficamos la relación entre el rango de las palabras y su frecuencia usando una escala logarítmica
freq_by_rank %>% 
  ggplot(aes(rank, term_frequency, color = State)) + 
  geom_line(linewidth = 1.1, alpha = 0.8) + 
  scale_x_log10() +  # Escala logarítmica para el rango
  scale_y_log10()    # Escala logarítmica para la frecuencia

# Realizamos una regresión lineal para analizar la relación Zipf y obtener un modelo estadístico
zipf <- lm(log10(term_frequency) ~ log10(rank), data = freq_by_rank)
# Resumen estadístico del modelo lineal para entender la relación Zipf
summary(zipf)
## 
## Call:
## lm(formula = log10(term_frequency) ~ log10(rank), data = freq_by_rank)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.31885 -0.09031 -0.00115  0.10115  0.27126 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -1.385637   0.015049  -92.08   <2e-16 ***
## log10(rank) -0.566917   0.007289  -77.78   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.1284 on 1572 degrees of freedom
## Multiple R-squared:  0.7937, Adjusted R-squared:  0.7936 
## F-statistic:  6049 on 1 and 1572 DF,  p-value: < 2.2e-16
# Representación visual de la regresión lineal con los parámetros del modelo, mostrando la relación de la Ley de Zipf
freq_by_rank %>% 
  ggplot(aes(rank, term_frequency, color = State)) + 
  geom_abline(intercept = coef(zipf)[1], slope = coef(zipf)[2],  # Utilizamos los coeficientes de la regresión
              color = "gray50", linetype = 2) +
  geom_line(linewidth = 1.1, alpha = 0.8) + 
  scale_x_log10() +  # Escala logarítmica para el rango
  scale_y_log10()    # Escala logarítmica para la frecuencia

La gráfica presenta la relación entre la frecuencia de los términos y su rango en los resúmenes de tiroteos para seis estados diferentes:

1. Categorías Cerca de la Línea de Rayas

  • Descripción: Las categorías que están cerca de la línea de rayas (la expectativa de la Ley de Zipf) están utilizando palabras cuyas frecuencias y rangos tienen una correlación esperada según esta ley.

  • Observación: En esta gráfica, las líneas para Arizona, California, Florida, North Carolina, Texas y Washington se agrupan bastante cerca de la línea de rayas, especialmente en los rangos bajos y medios. Esto sugiere que los resúmenes de los tiroteos en estos estados utilizan palabras de manera que sigue la distribución esperada por la Ley de Zipf. La cercanía a la línea de rayas indica un uso consistente de términos más frecuentes y menos frecuentes.

2. Categorías Por Encima de la Línea de Rayas

  • Descripción: Si una categoría se encuentra por encima de la línea de rayas, esto podría sugerir que las palabras en estos rangos tienen una frecuencia más alta de lo esperado por la Ley de Zipf.

  • Observación: En la gráfica, no hay muchas categorías significativamente por encima de la línea de rayas, pero hay puntos específicos donde algunas líneas de estados se elevan brevemente por encima de la línea. Las palabras en estos rangos podrían estar siendo utilizadas con mayor frecuencia, posiblemente debido a repeticiones de ciertos términos clave en los resúmenes de tiroteos. Es el caso de Arizona y North California por la zona central.

3. Categorías Por Debajo de la Línea de Rayas

  • Descripción: Las categorías que se ubican debajo de la línea de rayas están usando palabras con menos frecuencia de lo que predice la Ley de Zipf para sus rangos correspondientes.

  • Observación: Algunas líneas, especialmente en los rangos altos, se encuentran por debajo de la línea de rayas. Esto podría indicar una mayor diversidad de palabras utilizadas en los resúmenes de tiroteos, sugiriendo que los descripciones son variadas y menos focalizadas en ciertos términos. Es el caso de California en los rangos más altos.

En conclusión, la gráfica sugiere que la distribución de la frecuencia de las palabras en los resúmenes de tiroteos en estos seis estados sigue en gran medida la Ley de Zipf.

Creación de corpus y análisis de texto

Creación del corpus

Primero, creamos un corpus con docvars:

# Creamos un corpus con el contenido de los resúmenes
corpus_tiroteos <- quanteda::corpus(datos_limpios$Summary)

# Añadimos metadatos (docvars) al corpus
docvars(corpus_tiroteos, "Title") <- datos_limpios$Title
docvars(corpus_tiroteos, "Location") <- datos_limpios$Location
docvars(corpus_tiroteos, "Date") <- datos_limpios$Date
docvars(corpus_tiroteos, "Incident.Area") <- datos_limpios$Incident.Area
docvars(corpus_tiroteos, "Open.Close.Location") <- datos_limpios$Open.Close.Location
docvars(corpus_tiroteos, "Target") <- datos_limpios$Target
docvars(corpus_tiroteos, "Cause") <- datos_limpios$Cause
docvars(corpus_tiroteos, "Summary") <- datos_limpios$Summary
docvars(corpus_tiroteos, "Fatalities") <- datos_limpios$Fatalities
docvars(corpus_tiroteos, "Injured") <- datos_limpios$Injured
docvars(corpus_tiroteos, "Total.victims") <- datos_limpios$Total.victims
docvars(corpus_tiroteos, "Policeman.Killed") <- datos_limpios$Policeman.Killed
docvars(corpus_tiroteos, "Age") <- datos_limpios$Age
docvars(corpus_tiroteos, "Mental.Health.Issues") <- datos_limpios$Mental.Health.Issues
docvars(corpus_tiroteos, "Race") <- datos_limpios$Race
docvars(corpus_tiroteos, "Gender") <- datos_limpios$Gender
docvars(corpus_tiroteos, "Latitude") <- datos_limpios$Latitude
docvars(corpus_tiroteos, "Longitude") <- datos_limpios$Longitude
docvars(corpus_tiroteos, "City") <- datos_limpios$City
docvars(corpus_tiroteos, "State") <- datos_limpios$State

# Mostramos resumen del corpus con los metadatos añadidos
# summary(corpus_tiroteos)

Creación de los tokens y limpieza:

# Tokenización del corpus, limpiando el texto
tok_tiroteos <- quanteda::tokens(corpus_tiroteos,
                                 what = "word",
                                 remove_numbers = TRUE,
                                 remove_punct = TRUE,
                                 remove_symbols = TRUE,
                                 remove_separators = TRUE,
                                 remove_twitter = TRUE,
                                 remove_url = TRUE,
                                 include_docvars = TRUE)

# Eliminamos las stopwords en inglés
tok_tiroteos <- tokens_select(tok_tiroteos, 
                              pattern = stopwords("en"), 
                              selection = "remove")
# Definimos palabras clave relacionadas con los tiroteos
palabras_clave <- c("shooting", "victims", "gunman", "attack", "fire", "killed", "injured", "dead", "police", "suspect")

# Utilizamos la función kwic() para buscar las palabras clave en los tokens
kwic_tokens_tiroteos <- quanteda::kwic(tok_tiroteos, pattern = palabras_clave)
View(kwic_tokens_tiroteos)

Palabras frecuentes

Mostramos un gráfico donde se visualiza la frecuencia en el uso de palabras en el dataset:

# Creamos una matriz de DFM a partir de los tokens
# Esta matriz cuenta la frecuencia de cada término
dfm_tiroteos <- dfm(tok_tiroteos)

# Extracción de frecuencias de términos
frequencies <- textstat_frequency(dfm_tiroteos, n = 10)

# Visualización de las frecuencias
print(frequencies)
##     feature frequency rank docfreq group
## 1      shot       174    1     137   all
## 2    killed       168    2     137   all
## 3       two       158    3     131   all
## 4  shooting       113    4      97   all
## 5   shooter       110    5      94   all
## 6       man       108    6      84   all
## 7    people       107    7      91   all
## 8    police        99    8      84   all
## 9     three        95    9      79   all
## 10  killing        94   10      89   all
# Gráfico de barras de las palabras más frecuentes
grafico_frecuencia <- ggplot(frequencies, 
                             aes(x = reorder(feature, -frequency), 
                                 y = frequency, 
                                 fill = feature)) +
  geom_bar(stat = "identity") +
  coord_flip() +
  labs(x = "Palabras", y = "Frecuencia", title = "Frecuencia de Palabras en Resúmenes de Tiroteos") +
  theme_minimal() + # Usar un tema minimalista
  scale_fill_brewer(palette = "Set3") # Utilizar una paleta de colores de ColorBrewer

# Mostramos el gráfico
print(grafico_frecuencia)

El gráfico muestra la frecuencia de las palabras más comunes encontradas en los resúmenes de tiroteos:

  1. Palabras Más Frecuentes:

    • “shot”: Con una frecuencia de aproximadamente 180, es la palabra más común en los resúmenes. Esto indica que los tiroteos se describen frecuentemente en términos de disparos.

    • “killed”: Con una frecuencia similar a “shot”, esta palabra es muy utilizada para describir las fatalidades en los incidentes.

    • “two”: Esta palabra tiene una frecuencia notable, lo que podría indicar que en muchos incidentes se mencionan dos víctimas o dos disparos.

  2. Palabras Relacionadas con Víctimas y Perpetradores:

    • “man”, “people” y “shooter”: Estas palabras son comunes, lo que sugiere que los resúmenes se enfocan en describir tanto a los perpetradores como a las víctimas. “Man” y “shooter” indican la referencia frecuente a los atacantes, mientras que “people” puede referirse a las víctimas.

    • “police”: Su frecuencia muestra que la intervención policial es un tema recurrente en los resúmenes de los incidentes.

  3. Palabras que Describen el Evento:

    • “shooting” y “killing”: Estas palabras reflejan directamente la naturaleza de los incidentes y se utilizan para describir lo que ocurrió.

    • “three” y “two”: La aparición de números específicos sugiere que se mencionan con frecuencia la cantidad de víctimas o disparos en los resúmenes.

Palabras frecuentes agrupadas por Causa

# Extracción de frecuencias de términos por causa
frequencies <- textstat_frequency(dfm_tiroteos, n = 10, groups = Cause)

# Calculamos la frecuencia total de cada causa
causa_frecuencia <- frequencies %>%
  group_by(group) %>%
  summarise(total_frecuencia = sum(frequency)) %>%
  arrange(desc(total_frecuencia))

# Seleccionamos las 8 causas más comunes
top_causas <- causa_frecuencia %>%
  top_n(8, total_frecuencia) %>%
  pull(group)

# Filtramos las frecuencias para incluir solo las causas más comunes
frequencies_filtradas <- frequencies %>%
  filter(group %in% top_causas)

# Filtramos filas con 'group' vacío o NA
frequencies_filtradas <- frequencies_filtradas %>%
  filter(group != "" & !is.na(group))


# Gráfico de barras de las palabras más frecuentes agrupadas por las causas más comunes
grafico_frecuencia <- ggplot(frequencies_filtradas, 
                             aes(x = feature, 
                                 y = frequency, 
                                 fill = group)) +
  geom_bar(stat = "identity") +
  coord_flip() +   # Le damos la vuelta al grafico
  labs(x = "Palabras", y = "Frecuencia", title = "Frecuencia de Palabras Agrupadas por Causas Más Comunes")

# Mostramos el gráfico
print(grafico_frecuencia)

Las palabras con mayor frecuencia en los resúmenes de tiroteos varían según las causas más comunes. La causa “anger” se asocia principalmente con palabras como “shot”, “shooting” y “people,”, indicando incidentes que involucran disparos y personas. En los casos de “domestic dispute, las palabras frecuentes incluyen”officers”, “wife,” y “home,” lo que sugiere que estos incidentes a menudo ocurren en el hogar y pueden involucrar a la policía. La “frustration” está relacionada con palabras como “shcool”, “suicide” y “student” indicando tiroteos en centros educativos por alumnos frustrados por alguna causa.

Para la causa “psycho,” las palabras más comunes son “shot”, “shooting,” y “killed,” reflejando incidentes violentos que resultan en muertes y tiroteos. La “revenge” se asocia principalmente con “shooting” y “killed” sugiriendo que la venganza suele manifestarse en tiroteos. En los casos de “terrorism,” las palabras frecuentes son “shot”, “shooting”, “people”, “school”, “police” indicando que estos incidentes ocurren en entornos escolares y afectan a grupos de personas, con policiía involucrada. Finalmente, la causa “unemployment” está vinculada con palabras como “employee”, “former”, “fired” y “coworkers”, mostrando que los incidentes relacionados con el desempleo tienden a involucrar situaciones laborales y compañeros de trabajo.

Wordclouds

Creamos un wordcloud:

# Extraemos las 20 palabras más frecuentes de la DFM
topfeatures(dfm_tiroteos, 20)
##     shot   killed      two shooting  shooter      man   people   police 
##      174      168      158      113      110      108      107       99 
##    three  killing     fire  wounded   school     four   others   opened 
##       95       94       86       76       74       71       69       67 
##     home  student      one  injured 
##       66       65       62       59
# Extraemos las 100 palabras más frecuentes de la DFM para un análisis más detallado
topfeatures(dfm_tiroteos, 100)
##       shot     killed        two   shooting    shooter        man     people 
##        174        168        158        113        110        108        107 
##     police      three    killing       fire    wounded     school       four 
##         99         95         94         86         76         74         71 
##     others     opened       home    student        one    injured      later 
##         69         67         66         65         62         59         57 
##   arrested      began    entered        old     former    suicide   students 
##         52         51         49         49         47         47         47 
##    another   wounding      fired      house       five       wife    arrived 
##         45         43         41         40         38         35         35 
##        car      scene       went committing        gun      party   february 
##         33         33         32         30         28         28         28 
##       high        six      found   injuring     gunman       dead    fatally 
##         28         27         26         26         26         25         25 
##    outside     office    officer     family   december    teacher      woman 
##         25         25         24         24         24         24         23 
##    morning university   children       died     inside   officers    victims 
##         23         23         21         20         20         20         20 
## california  apartment       fled      april        men   building     mother 
##         20         19         19         19         18         18         18 
##    october      march   employee      early     campus     august      hours 
##         18         18         17         17         17         17         16 
##     street      group      south  including       left        law       club 
##         16         16         16         16         15         15         15 
##        new   november        may    january     postal girlfriend      young 
##         15         15         15         15         15         14         14 
##      seven    members       post      drove     person  coworkers     worker 
##         14         14         14         13         13         13         13 
##  employees    started 
##         13         13
# Establecemos una semilla para la generación de números aleatorios
set.seed(100)

# Creamos un wordcloud con la matriz de términos por documento
textplot_wordcloud(dfm_tiroteos, 
                   min_count = 10,    # Solo mostramos palabras con al menos 10 ocurrencias
                   random_order = FALSE, # Colocamos las palabras más frecuentes en el centro
                   rotation = 0,         # Las palabras se mostrarán sin rotación (horizontalmente)
                   color = RColorBrewer::brewer.pal(8,"Spectral")) # Usamos una paleta de colores predefinida

La nube de palabras destaca las principales palabras asociadas con los resúmenes de tiroteos en Estados Unidos. Palabras como “killed”, “shot”, “shooter”, “police”, y “school” son particularmente frecuentes, subrayando la gravedad y el impacto de estos eventos en la sociedad. La prominencia de términos como “two”, “people”, “wounded”, y “injured” también revela la extensión del daño y el alcance de las víctimas en estos incidentes.

Ahora, generamos nuevos wordclouds con bigramas para comparar con el anterior:

# Ahora probamos a hacerlo con bigramas
tok_tiroteos_2 <- tokens_ngrams(tok_tiroteos, n = 2)

# Creamos una matriz de DFM a partir de los tokens
# Esta matriz cuenta la frecuencia de cada término
dfm_tiroteos_2 <- dfm(tok_tiroteos_2)

# Extraemos las 20 palabras más frecuentes de la DFM
topfeatures(dfm_tiroteos_2, 20)
##        opened_fire        shot_killed     began_shooting committing_suicide 
##                 67                 50                 43                 30 
##        high_school       three_people        four_people         two_others 
##                 28                 20                 20                 19 
##     police_officer       fatally_shot            old_man        killing_two 
##                 19                 18                 18                 16 
##      killed_police         fled_scene       three_others        post_office 
##                 15                 13                 13                 13 
##       killed_three           man_shot     shooter_killed         killed_two 
##                 12                 12                 12                 12
# Extraemos las 100 palabras más frecuentes de la DFM para un análisis más detallado
topfeatures(dfm_tiroteos_2, 100)
##         opened_fire         shot_killed      began_shooting  committing_suicide 
##                  67                  50                  43                  30 
##         high_school        three_people         four_people          two_others 
##                  28                  20                  20                  19 
##      police_officer        fatally_shot             old_man         killing_two 
##                  19                  18                  18                  16 
##       killed_police          fled_scene        three_others         post_office 
##                  15                  13                  13                  13 
##        killed_three            man_shot      shooter_killed          killed_two 
##                  12                  12                  12                  12 
##        two_students      later_arrested      people_wounded         killed_four 
##                  12                  12                  11                  11 
##   committed_suicide         man_entered       killing_three             two_men 
##                  11                  11                  10                   9 
##          shot_three     law_enforcement       shooter_fired    people_including 
##                   9                   9                   9                   9 
##        wounding_two          found_dead         wounded_two         turning_gun 
##                   9                   8                   8                   8 
##       wounded_three      people_injured            wife_two         four_others 
##                   8                   8                   8                   8 
##         parking_lot         killing_one            man_went   elementary_school 
##                   8                   8                   8                   8 
##            shot_two       early_morning        shooter_shot         killed_wife 
##                   7                   7                   7                   7 
##          fired_upon    started_shooting        shot_injured        wounded_four 
##                   7                   7                   7                   7 
##         five_people          two_people     people_injuring     injured_another 
##                   7                   7                   7                   7 
##    shooting_killing 14-year-old_student      arrived_campus     others_arrested 
##                   7                   7                   7                   7 
##          later_shot           shot_four         people_shot         house_party 
##                   6                   6                   6                   6 
##      family_members         gunman_shot     police_officers      south_carolina 
##                   6                   6                   6                   6 
##         fire_inside   shooting_students       middle_school     wounded_another 
##                   6                   6                   6                   6 
##       postal_worker         hours_later  apprehended_police        group_people 
##                   6                   5                   5                   5 
##             one_man       people_killed        two_children      injuring_three 
##                   5                   5                   5                   5 
##          six_people       wounding_four          man_opened         another_man 
##                   5                   5                   5                   5 
##         killed_five         five_others    state_university          old_former 
##                   5                   5                   5                   5 
##        killing_four       shooter_later        two_wounding      estranged_wife 
##                   5                   5                   5                   5 
##         killing_six     student_entered     arrested_police       fleeing_scene 
##                   5                   5                   5                   5 
##      arrived_school 15-year-old_student     shooting_killed    former_workplace 
##                   5                   5                   5                   5
# Establecemos una semilla para la generación de números aleatorios
set.seed(100)

# Creamos un wordcloud con la matriz de términos por documento
textplot_wordcloud(dfm_tiroteos_2, 
                   min_count = 5,    # Solo mostramos palabras con al menos 5 ocurrencias
                   random_order = FALSE, # Colocamos las palabras más frecuentes en el centro
                   rotation = 0,         # Las palabras se mostrarán sin rotación (horizontalmente)
                   color = RColorBrewer::brewer.pal(8,"Spectral")) # Usamos una paleta de colores predefinida

La nube de palabras con bigramas destaca términos clave relacionados con incidentes de tiroteos en Estados Unidos. Bigrams como “opened fire”, “shot killed”, y “began shooting” describen las acciones violentas y sus consecuencias. Términos como “high school”, “elementary school”, y “police officer” subrayan los entornos y las personas frecuentemente involucradas. Además, “committing suicide” y “fatally shot” reflejan el desenlace trágico de muchos de estos eventos.

Creación de corpus y análisis de texto para causas determinadas

Filtrado por causas

Nos centramos en tres causas determinadas de interés para el estudio posterior.

He elegido psycho porque es una causa muy común de tiroteos. Además, he decidido añadir terrorism porque he visto que en el anterior gráfico aparecía con bastante frecuencia y sería interesante estudiar de qué tipos de crímenes se trata.

Por último, he elegido estudiar domestic dispute porque es una causa muy común pero diferente a las demás, ya que suelen tratarse de crímenes más pasionales y que ocurren en el ámbito doméstico

# Causas de interes
interesantes <- c("terrorism", "psycho", "domestic dispute")

# Preparación del gráfico 'grafico_frecuencia_2' con filtrado para causas específicas
grafico_frecuencia_2 <- ggplot(
  data = frequencies_filtradas[frequencies_filtradas$group %in% interesantes, ], 
                             aes(x = fct_reorder(feature, frequency), 
                                 y = frequency, 
                                 fill = group)) +
  geom_bar(stat = "identity") +
  coord_flip() +  # Le damos la vuelta al grafico
  labs(x = "Palabras", y = "Frecuencia", title = "Frecuencia de Palabras Agrupadas por Causas de Interés")

# Mostramos el grafico
print(grafico_frecuencia_2)

La palabra “killed” (asesinado) es muy frecuente en los resúmenes relacionados con problemas psicológicos y terrorismo, indicando que estos incidentes a menudo resultan en muertes. La palabra “people” (personas) es prominente en los resúmenes de terrorismo, subrayando que estos eventos suelen involucrar a múltiples personas. Palabras como “shooting” (tiroteo) y “shot” (disparado) son frecuentes en incidentes relacionados tanto con problemas psicológicos como con terrorismo, reflejando la naturaleza violenta de estos eventos. Además, términos como “wife” (esposa) y “girlfriend” (novia) y “home” (casa) son relevantes para domestic dispute, lo que indica que son crímenes relacionados con personas cercanas y dentro del hogar.

Estudiamos TERRORISM

Extraemos un subconjunto de datos en el que solo seleccionamos aquellos con la causa de terrorism:

# Extraemos subconjunto de terrorism
datos_terrorism <- subset(datos_limpios,
                                  Cause == "terrorism")

# Guardamos el conjunto de datos de terrorismo
save(datos_terrorism, file = "datos_terrorism.rda")

Creamos el corpus y los tokens:

# Creamos un corpus con el contenido de los resúmenes
corpus_tiroteos_terrorism <- quanteda::corpus(datos_terrorism$Summary)

# Añadimos metadatos (docvars) al corpus
docvars(corpus_tiroteos_terrorism, "Title") <- datos_terrorism$Title
docvars(corpus_tiroteos_terrorism, "Location") <- datos_terrorism$Location
docvars(corpus_tiroteos_terrorism, "Date") <- datos_terrorism$Date
docvars(corpus_tiroteos_terrorism, "Incident.Area") <- datos_terrorism$Incident.Area
docvars(corpus_tiroteos_terrorism, "Open.Close.Location") <- datos_terrorism$Open.Close.Location
docvars(corpus_tiroteos_terrorism, "Target") <- datos_terrorism$Target
docvars(corpus_tiroteos_terrorism, "Cause") <- datos_terrorism$Cause
docvars(corpus_tiroteos_terrorism, "Summary") <- datos_terrorism$Summary
docvars(corpus_tiroteos_terrorism, "Fatalities") <- datos_terrorism$Fatalities
docvars(corpus_tiroteos_terrorism, "Injured") <- datos_terrorism$Injured
docvars(corpus_tiroteos_terrorism, "Total.victims") <- datos_terrorism$Total.victims
docvars(corpus_tiroteos_terrorism, "Policeman.Killed") <- datos_terrorism$Policeman.Killed
docvars(corpus_tiroteos_terrorism, "Age") <- datos_terrorism$Age
docvars(corpus_tiroteos_terrorism, "Mental.Health.Issues") <- datos_terrorism$Mental.Health.Issues
docvars(corpus_tiroteos_terrorism, "Race") <- datos_terrorism$Race
docvars(corpus_tiroteos_terrorism, "Gender") <- datos_terrorism$Gender
docvars(corpus_tiroteos_terrorism, "Latitude") <- datos_terrorism$Latitude
docvars(corpus_tiroteos_terrorism, "Longitude") <- datos_terrorism$Longitude
docvars(corpus_tiroteos_terrorism, "City") <- datos_terrorism$City
docvars(corpus_tiroteos_terrorism, "State") <- datos_terrorism$State

# Mostramos resumen del corpus con los metadatos añadidos
# summary(corpus_tiroteos_terrorism)

# Tokenización del corpus, limpiando el texto
tok_tiroteos_terrorism <- quanteda::tokens(corpus_tiroteos_terrorism,
                                 what = "word",
                                 remove_numbers = TRUE,
                                 remove_punct = TRUE,
                                 remove_symbols = TRUE,
                                 remove_separators = TRUE,
                                 remove_twitter = TRUE,
                                 remove_url = TRUE,
                                 include_docvars = TRUE)

# Utilizamos la función kwic() para buscar las palabras clave en los tokens
kwic_tokens_tiroteos_terrorism <- quanteda::kwic(tok_tiroteos_terrorism, pattern = palabras_clave)
View(kwic_tokens_tiroteos_terrorism)

# Eliminamos las stopwords en inglés
tok_tiroteos_terrorism <- tokens_select(tok_tiroteos_terrorism, 
                              pattern = stopwords("en"), 
                              selection = "remove")

Creamos la DFM y el wordcloud:

# Creamos una matriz de DFM a partir de los tokens
# Esta matriz cuenta la frecuencia de cada término
dfm_tiroteos_terrorism <- dfm(tok_tiroteos_terrorism)

# Extraemos las 20 palabras más frecuentes de la DFM
topfeatures(dfm_tiroteos_terrorism, 20)
##   killed shooting   people      two   school     shot     fire   police 
##       36       33       33       31       29       28       25       22 
##   opened    three  shooter  killing  wounded  entered   others  student 
##       21       20       19       18       17       17       16       16 
##  injured      one    began      man 
##       15       14       14       12
# Extraemos las 100 palabras más frecuentes de la DFM para un análisis más detallado
topfeatures(dfm_tiroteos_terrorism, 100)
##     killed   shooting     people        two     school       shot       fire 
##         36         33         33         31         29         28         25 
##     police     opened      three    shooter    killing    wounded    entered 
##         22         21         20         19         18         17         17 
##     others    student    injured        one      began        man      later 
##         16         16         15         14         14         12         12 
##       five   arrested   students   wounding university    arrived      fired 
##         11         11         11         10         10         10          9 
##     gunman       high    another        six     center       home    victims 
##          9          9          8          7          7          7          7 
##      house      party   shooters    morning    outside       hall        old 
##          7          7          7          7          7          7          7 
##       fled      scene      woman   december   injuring   building       four 
##          6          6          6          6          6          6          6 
## committing    suicide   children    fatally critically       died     street 
##          6          6          6          5          5          5          5 
##       dead  including    october   february     campus elementary       city 
##          5          5          5          5          5          5          5 
##     former california        car       left     gunmen     inside      early 
##          5          5          4          4          4          4          4 
##      south       room       told      seven    teacher    opening      april 
##          4          4          4          4          4          4          4 
##    january   november      texas  custodian     worker  employees        gun 
##          4          4          4          4          3          3          3 
##      women department      taken    seattle      hours      group        men 
##          3          3          3          3          3          3          3 
##       came  shootings   saturday       team       head  suspected  apartment 
##          3          3          3          3          3          3          3 
##    several     health 
##          3          3
# Establecemos una semilla para la generación de números aleatorios
set.seed(100)

# Creamos un wordcloud con la matriz de términos por documento
textplot_wordcloud(dfm_tiroteos_terrorism, 
                   min_count = 2,    # Solo mostramos palabras con al menos 2 ocurrencias
                   random_order = FALSE, # Colocamos las palabras más frecuentes en el centro
                   rotation = 0,         # Las palabras se mostrarán sin rotación (horizontalmente)
                   color = RColorBrewer::brewer.pal(8,"Spectral")) # Usamos una paleta de colores predefinida

Análisis de la nube de palabras para la causa “terrorism”:

La nube de palabras de los tiroteos con causa “terrorismo” revela un patrón de eventos altamente letales y disruptivos, con un impacto significativo en personas y lugares públicos, especialmente en entornos educativos. La presencia prominente de términos como “killed”, “people”, “shooting”, y “school” subraya la gravedad y el contexto común de estos ataques.

Ahora, generamos un nuevo worldcloud con bigramas para comparar con el anterior:

# Ahora probamos a hacerlo con bigramas
tok_tiroteos_terrorism_2 <- tokens_ngrams(tok_tiroteos_terrorism, n = 2)

# Creamos una matriz de DFM a partir de los tokens
# Esta matriz cuenta la frecuencia de cada término
dfm_tiroteos_terrorism_2 <- dfm(tok_tiroteos_terrorism_2)

# Extraemos las 20 palabras más frecuentes de la DFM
topfeatures(dfm_tiroteos_terrorism_2, 20)
##        opened_fire     began_shooting        shot_killed        high_school 
##                 21                 13                 11                  9 
##       three_people committing_suicide         fled_scene         two_others 
##                  6                  6                  5                  5 
##      killed_police  elementary_school       fatally_shot      people_killed 
##                  5                  5                  4                  4 
##         two_gunmen     shooter_killed        five_people   people_including 
##                  4                  4                  3                  3 
##         two_people       killed_three     police_officer     people_wounded 
##                  3                  3                  3                  3
# Extraemos las 100 palabras más frecuentes de la DFM para un análisis más detallado
topfeatures(dfm_tiroteos_terrorism_2, 100)
##          opened_fire       began_shooting          shot_killed 
##                   21                   13                   11 
##          high_school         three_people   committing_suicide 
##                    9                    6                    6 
##           fled_scene           two_others        killed_police 
##                    5                    5                    5 
##    elementary_school         fatally_shot        people_killed 
##                    5                    4                    4 
##           two_gunmen       shooter_killed          five_people 
##                    4                    4                    3 
##     people_including           two_people         killed_three 
##                    3                    3                    3 
##       police_officer       people_wounded          killed_five 
##                    3                    3                    3 
##       police_arrived    shooting_students       people_injured 
##                    3                    3                    3 
##      student_entered           killed_two         two_students 
##                    3                    3                    3 
##       arrived_school           shot_three         fired_rounds 
##                    3                    2                    2 
##          turning_gun      law_enforcement         killing_five 
##                    2                    2                    2 
##        five_wounding         wounding_six   critically_wounded 
##                    2                    2                    2 
##     wounded_shooting           fired_upon        shooter_fired 
##                    2                    2                    2 
##   saturday_afternoon       pregnant_woman        one_suspected 
##                    2                    2                    2 
##          los_angeles     killing_wounding      inland_regional 
##                    2                    2                    2 
##      regional_center   entered_conference      conference_room 
##                    2                    2                    2 
##      people_injuring        three_victims         victims_shot 
##                    2                    2                    2 
##      shootout_police        gunman_opened          told_people 
##                    2                    2                    2 
##         opening_fire      officer_injured    shooting_occurred 
##                    2                    2                    2 
##        morning_april          fire_inside         seven_people 
##                    2                    2                    2 
##        early_morning         two_shooters      morning_january 
##                    2                    2                    2 
##          five_others     state_university       injuring_three 
##                    2                    2                    2 
##          man_entered     shooting_wounded       killing_twelve 
##                    2                    2                    2 
##      school_shooting          shots_fired          22-year_old 
##                    2                    2                    2 
##       later_arrested     arrested_outside         three_others 
##                    2                    2                    2 
##       others_wounded      wounded_shooter         ages_arrived 
##                    2                    2                    2 
##   steven_kazmierczak   kazmierczak_opened         fire_lecture 
##                    2                    2                    2 
##         lecture_hall            hall_shot            city_hall 
##                    2                    2                    2 
##      council_members         wounding_two     18-year-old_high 
##                    2                    2                    2 
##  15-year-old_student         school_began  shooting_classmates 
##                    2                    2                    2 
##          taking_life      students_killed        taken_custody 
##                    2                    2                    2 
##          clinic_shot          killing_one cleveland_elementary 
##                    2                    2                    2 
##      injured_another     children_waiting     rose-mar_college 
##                    2                    2                    2 
##       college_beauty 
##                    2
# Establecemos una semilla para la generación de números aleatorios
set.seed(100)

# Creamos un wordcloud con la matriz de términos por documento
textplot_wordcloud(dfm_tiroteos_terrorism_2, 
                   min_count = 2,    # Solo mostramos palabras con al menos 2 ocurrencias
                   random_order = FALSE, # Colocamos las palabras más frecuentes en el centro
                   rotation = 0,         # Las palabras se mostrarán sin rotación (horizontalmente)
                   color = RColorBrewer::brewer.pal(8,"Spectral")) # Usamos una paleta de colores predefinida

La nube de palabras con bigramas resalta frases clave relacionadas con los ataques terroristas en Estados Unidos. Bigramas como “opened fire”, “shot killed”, y “began shooting” describen las acciones violentas y sus consecuencias. Términos como “high school”, “elementary school”, y “police officer” subrayan los entornos y las personas frecuentemente involucradas. Además, bigramas como “committing suicide” y “fatally shot” reflejan el desenlace trágico de muchos de estos eventos.

Este wordcloud se parece mucho al wordcloud de bigramas de las causas más comunes, por lo que ahora sabemos que los términos más frecuentes que nos aparecieron en ese wordcloud eran de terrorismo.

Estudiamos PSYCHO

Extraemos un subconjunto de datos en el que solo seleccionamos aquellos con la causa de psycho:

# Extraemos subconjunto de psycho
datos_psycho <- subset(datos_limpios,
                                  Cause == "psycho")

# Guardamos el conjunto de datos de psycho
save(datos_psycho, file = "datos_psycho.rda")

Creamos el corpus y los tokens:

# Creamos un corpus con el contenido de los resúmenes
corpus_tiroteos_psycho <- quanteda::corpus(datos_psycho$Summary)

# Añadimos metadatos (docvars) al corpus
docvars(corpus_tiroteos_psycho, "Title") <- datos_psycho$Title
docvars(corpus_tiroteos_psycho, "Location") <- datos_psycho$Location
docvars(corpus_tiroteos_psycho, "Date") <- datos_psycho$Date
docvars(corpus_tiroteos_psycho, "Incident.Area") <- datos_psycho$Incident.Area
docvars(corpus_tiroteos_psycho, "Open.Close.Location") <- datos_psycho$Open.Close.Location
docvars(corpus_tiroteos_psycho, "Target") <- datos_psycho$Target
docvars(corpus_tiroteos_psycho, "Cause") <- datos_psycho$Cause
docvars(corpus_tiroteos_psycho, "Summary") <- datos_psycho$Summary
docvars(corpus_tiroteos_psycho, "Fatalities") <- datos_psycho$Fatalities
docvars(corpus_tiroteos_psycho, "Injured") <- datos_psycho$Injured
docvars(corpus_tiroteos_psycho, "Total.victims") <- datos_psycho$Total.victims
docvars(corpus_tiroteos_psycho, "Policeman.Killed") <- datos_psycho$Policeman.Killed
docvars(corpus_tiroteos_psycho, "Age") <- datos_psycho$Age
docvars(corpus_tiroteos_psycho, "Mental.Health.Issues") <- datos_psycho$Mental.Health.Issues
docvars(corpus_tiroteos_psycho, "Race") <- datos_psycho$Race
docvars(corpus_tiroteos_psycho, "Gender") <- datos_psycho$Gender
docvars(corpus_tiroteos_psycho, "Latitude") <- datos_psycho$Latitude
docvars(corpus_tiroteos_psycho, "Longitude") <- datos_psycho$Longitude
docvars(corpus_tiroteos_psycho, "City") <- datos_psycho$City
docvars(corpus_tiroteos_psycho, "State") <- datos_psycho$State

# Mostramos resumen del corpus con los metadatos añadidos
# summary(corpus_tiroteos_psycho)

# Tokenización del corpus, limpiando el texto
tok_tiroteos_psycho <- quanteda::tokens(corpus_tiroteos_psycho,
                                 what = "word",
                                 remove_numbers = TRUE,
                                 remove_punct = TRUE,
                                 remove_symbols = TRUE,
                                 remove_separators = TRUE,
                                 remove_twitter = TRUE,
                                 remove_url = TRUE,
                                 include_docvars = TRUE)

# Utilizamos la función kwic() para buscar las palabras clave en los tokens
kwic_tokens_tiroteos_psycho <- quanteda::kwic(tok_tiroteos_psycho, pattern = palabras_clave)
View(kwic_tokens_tiroteos_psycho)

# Eliminamos las stopwords en inglés
tok_tiroteos_psycho <- tokens_select(tok_tiroteos_psycho, 
                              pattern = stopwords("en"), 
                              selection = "remove")

Creamos la DFM y el wordcloud:

# Creamos una matriz de DFM a partir de los tokens
# Esta matriz cuenta la frecuencia de cada término
dfm_tiroteos_psycho <- dfm(tok_tiroteos_psycho)

# Extraemos las 20 palabras más frecuentes de la DFM
topfeatures(dfm_tiroteos_psycho, 20)
##     shot   killed      two      man  killing     home   police  shooter 
##       56       50       46       34       33       32       30       30 
## shooting    three   others     went      old     fire    later  wounded 
##       26       22       20       19       18       17       17       15 
##   school     four   people  suicide 
##       15       15       14       14
# Extraemos las 100 palabras más frecuentes de la DFM para un análisis más detallado
topfeatures(dfm_tiroteos_psycho, 100)
##          shot        killed           two           man       killing 
##            56            50            46            34            33 
##          home        police       shooter      shooting         three 
##            32            30            30            26            22 
##        others          went           old          fire         later 
##            20            19            18            17            17 
##       wounded        school          four        people       suicide 
##            15            15            15            14            14 
##       another          wife        mother         house        family 
##            14            13            12            11            11 
##      arrested      wounding       student       entered           car 
##            11            11            11            10            10 
##       injured         found          five         began        opened 
##             9             9             9             9             9 
##      students       parents      injuring        august        former 
##             9             8             8             8             8 
##       january        office    california          head          high 
##             8             8             8             7             7 
##           gun           one      children    committing           may 
##             7             7             7             7             7 
##           new      february           day        worked       outside 
##             7             7             7             6             6 
##         scene       officer       members        father         march 
##             6             6             6             6             6 
##         drove         santa       teacher         fired          fled 
##             6             6             6             5             5 
##       rampage         early       fatally ex-girlfriend      building 
##             5             5             5             5             5 
##       ex-wife         death       several      december         woman 
##             5             5             5             5             5 
##     committed         spree     estranged    unemployed          dead 
##             5             5             5             5             4 
##        nearby     wednesday   grandmother         local      daughter 
##             4             4             4             4             4 
##        county          died          days        person          post 
##             4             4             4             4             4 
##       meeting        worker       arrived       victims        campus 
##             4             4             4             4             4 
##         hours      carolina         seven      returned        random 
##             4             4             4             3             3
# Establecemos una semilla para la generación de números aleatorios
set.seed(100)

# Creamos un wordcloud con la matriz de términos por documento
textplot_wordcloud(dfm_tiroteos_psycho, 
                   min_count = 2,    # Solo mostramos palabras con al menos 2 ocurrencias
                   random_order = FALSE, # Colocamos las palabras más frecuentes en el centro
                   rotation = 0,         # Las palabras se mostrarán sin rotación (horizontalmente)
                   color = RColorBrewer::brewer.pal(8,"Spectral")) # Usamos una paleta de colores predefinida

Análisis de la nube de palabras para la causa “psycho”:

La presencia prominente de términos como “killed”, “shot”, “shooter”, y “police” subraya la violencia y la frecuencia de la intervención policial en estos incidentes. Palabras como “home” y “student” sugieren que estos tiroteos a menudo ocurren en entornos familiares y educativos, respectivamente. Además, la mención frecuente de “suicide” refleja la tendencia de los perpetradores a quitarse la vida después de cometer los actos violentos.

Ahora, generamos un nuevo worldcloud con bigramas para comparar con el anterior:

# Ahora probamos a hacerlo con bigramas
tok_tiroteos_psycho_2 <- tokens_ngrams(tok_tiroteos_psycho, n = 2)

# Creamos una matriz de DFM a partir de los tokens
# Esta matriz cuenta la frecuencia de cada término
dfm_tiroteos_psycho_2 <- dfm(tok_tiroteos_psycho_2)

# Extraemos las 20 palabras más frecuentes de la DFM
topfeatures(dfm_tiroteos_psycho_2, 20)
##        shot_killed        opened_fire     began_shooting            old_man 
##                 14                  9                  8                  8 
##        high_school committing_suicide        killing_two       three_people 
##                  7                  7                  7                  6 
##     police_officer        man_entered      killing_three     family_members 
##                  6                  6                  5                  5 
##         two_others  committed_suicide           man_shot       two_children 
##                  5                  5                  4                  4 
##           man_went     estranged_wife           wife_two       shot_injured 
##                  4                  4                  3                  3
# Extraemos las 100 palabras más frecuentes de la DFM para un análisis más detallado
topfeatures(dfm_tiroteos_psycho_2, 100)
##          shot_killed          opened_fire       began_shooting 
##                   14                    9                    8 
##              old_man          high_school   committing_suicide 
##                    8                    7                    7 
##          killing_two         three_people       police_officer 
##                    7                    6                    6 
##          man_entered        killing_three       family_members 
##                    6                    5                    5 
##           two_others    committed_suicide             man_shot 
##                    5                    5                    4 
##         two_children             man_went       estranged_wife 
##                    4                    4                    4 
##             wife_two         shot_injured          family_home 
##                    3                    3                    3 
##         shooter_shot     fatally_shooting          wounded_two 
##                    3                    3                    3 
##          two_cousins        shooter_drove             shot_two 
##                    3                    3                    3 
##           shot_three           days_later      injured_another 
##                    3                    3                    3 
##       another_person          later_found    others_committing 
##                    3                    3                    3 
##          44-year_old         killed_three       later_arrested 
##                    3                    3                    3 
##          four_people          four_others        shooter_later 
##                    3                    3                    3 
##         santa_monica       shooter_killed         two_wounding 
##                    3                    3                    3 
##         wounding_two          wife_worked           killed_two 
##                    3                    3                    3 
##       north_carolina          post_office      others_arrested 
##                    3                    3                    3 
##    domestic_violence         shooting_man           man_killed 
##                    2                    2                    2 
##          killed_wife            shot_head             son_shot 
##                    2                    2                    2 
##           house_fire       killed_shooter       injuring_three 
##                    2                    2                    2 
##           fled_scene    confronted_police         officer_shot 
##                    2                    2                    2 
##           mother_two        two_neighbors            two_young 
##                    2                    2                    2 
##          home_killed          killed_four          turning_gun 
##                    2                    2                    2 
##        mother_sister            also_shot         shot_shooter 
##                    2                    2                    2 
##        killed_mother         georgia_home          may_shooter 
##                    2                    2                    2 
##      shooter_entered ex-girlfriend's_home     shooting_killing 
##                    2                    2                    2 
##            open_fire         shooter_left          gunned_four 
##                    2                    2                    2 
##          four_family           new_mexico          two_teenage 
##                    2                    2                    2 
##         february_man         entered_home        drove_another 
##                    2                    2                    2 
##       another_county           found_dead        children_went 
##                    2                    2                    2 
##           found_shot         three_family        early_morning 
##                    2                    2                    2 
##          january_man        two_daughters         shooter_went 
##                    2                    2                    2 
##         december_man         fatally_shot             two_days 
##                    2                    2                    2 
##           two_police      police_officers        killed_police 
##                    2                    2                    2 
##         people_death 
##                    2
# Establecemos una semilla para la generación de números aleatorios
set.seed(100)

# Creamos un wordcloud con la matriz de términos por documento
textplot_wordcloud(dfm_tiroteos_psycho_2, 
                   min_count = 2,    # Solo mostramos palabras con al menos 2 ocurrencias
                   random_order = FALSE, # Colocamos las palabras más frecuentes en el centro
                   rotation = 0,         # Las palabras se mostrarán sin rotación (horizontalmente)
                   color = RColorBrewer::brewer.pal(8,"Spectral")) # Usamos una paleta de colores predefinida

La nube de palabras con bigramas para la causa “psycho” revela un patrón de eventos letales y perturbadores, con un impacto significativo tanto en términos de muertes como de heridos. Bigrams como “shot killed”, “opened fire”, y “began shooting” describen las acciones violentas y sus consecuencias. Términos como “police officer”, “high school”, y “family members” subrayan los entornos y las personas frecuentemente involucradas. Además, bigramas como “committing suicide” y “committed suicide” reflejan la tendencia de los perpetradores a quitarse la vida después de cometer los actos violentos.

Estudiamos DOMESTIC DISPUTE

Extraemos un subconjunto de datos en el que solo seleccionamos aquellos con la causa de domestic dispute:

# Extraemos subconjunto de domestic dispute
datos_domestic_dispute <- subset(datos_limpios,
                                  Cause == "domestic dispute")

# Guardamos el conjunto de datos de domestic dispute
save(datos_domestic_dispute, file = "datos_domestic_dispute.rda")

Creamos el corpus y los tokens:

# Creamos un corpus con el contenido de los resúmenes
corpus_tiroteos_domestic_dispute <- quanteda::corpus(datos_domestic_dispute$Summary)

# Añadimos metadatos (docvars) al corpus
docvars(corpus_tiroteos_domestic_dispute, "Title") <- datos_domestic_dispute$Title
docvars(corpus_tiroteos_domestic_dispute, "Location") <- datos_domestic_dispute$Location
docvars(corpus_tiroteos_domestic_dispute, "Date") <- datos_domestic_dispute$Date
docvars(corpus_tiroteos_domestic_dispute, "Incident.Area") <- datos_domestic_dispute$Incident.Area
docvars(corpus_tiroteos_domestic_dispute, "Open.Close.Location") <- datos_domestic_dispute$Open.Close.Location
docvars(corpus_tiroteos_domestic_dispute, "Target") <- datos_domestic_dispute$Target
docvars(corpus_tiroteos_domestic_dispute, "Cause") <- datos_domestic_dispute$Cause
docvars(corpus_tiroteos_domestic_dispute, "Summary") <- datos_domestic_dispute$Summary
docvars(corpus_tiroteos_domestic_dispute, "Fatalities") <- datos_domestic_dispute$Fatalities
docvars(corpus_tiroteos_domestic_dispute, "Injured") <- datos_domestic_dispute$Injured
docvars(corpus_tiroteos_domestic_dispute, "Total.victims") <- datos_domestic_dispute$Total.victims
docvars(corpus_tiroteos_domestic_dispute, "Policeman.Killed") <- datos_domestic_dispute$Policeman.Killed
docvars(corpus_tiroteos_domestic_dispute, "Age") <- datos_domestic_dispute$Age
docvars(corpus_tiroteos_domestic_dispute, "Mental.Health.Issues") <- datos_domestic_dispute$Mental.Health.Issues
docvars(corpus_tiroteos_domestic_dispute, "Race") <- datos_domestic_dispute$Race
docvars(corpus_tiroteos_domestic_dispute, "Gender") <- datos_domestic_dispute$Gender
docvars(corpus_tiroteos_domestic_dispute, "Latitude") <- datos_domestic_dispute$Latitude
docvars(corpus_tiroteos_domestic_dispute, "Longitude") <- datos_domestic_dispute$Longitude
docvars(corpus_tiroteos_domestic_dispute, "City") <- datos_domestic_dispute$City
docvars(corpus_tiroteos_domestic_dispute, "State") <- datos_domestic_dispute$State

# Mostramos resumen del corpus con los metadatos añadidos
# summary(corpus_tiroteos_domestic_dispute)

# Tokenización del corpus, limpiando el texto
tok_tiroteos_domestic_dispute <- quanteda::tokens(corpus_tiroteos_domestic_dispute,
                                 what = "word",
                                 remove_numbers = TRUE,
                                 remove_punct = TRUE,
                                 remove_symbols = TRUE,
                                 remove_separators = TRUE,
                                 remove_twitter = TRUE,
                                 remove_url = TRUE,
                                 include_docvars = TRUE)

# Utilizamos la función kwic() para buscar las palabras clave en los tokens
kwic_tokens_tiroteos_domestic_dispute <- quanteda::kwic(tok_tiroteos_domestic_dispute, pattern = palabras_clave)
View(kwic_tokens_tiroteos_domestic_dispute)

# Eliminamos las stopwords en inglés
tok_tiroteos_domestic_dispute <- tokens_select(tok_tiroteos_domestic_dispute, 
                              pattern = stopwords("en"), 
                              selection = "remove")

Creamos la DFM y el wordcloud:

# Creamos una matriz de DFM a partir de los tokens
# Esta matriz cuenta la frecuencia de cada término
dfm_tiroteos_domestic_dispute <- dfm(tok_tiroteos_domestic_dispute)

# Extraemos las 20 palabras más frecuentes de la DFM
topfeatures(dfm_tiroteos_domestic_dispute, 20)
##        two        man       shot       wife       home     killed girlfriend 
##         10          8          8          7          6          5          5 
##   officers     friday      three   domestic       sons    morning    victims 
##          5          4          4          4          3          3          3 
##     inside      house   daughter    shooter   standoff     gunman 
##          3          3          3          3          3          3
# Extraemos las 100 palabras más frecuentes de la DFM para un análisis más detallado
topfeatures(dfm_tiroteos_domestic_dispute, 100)
##            two            man           shot           wife           home 
##             10              8              8              7              6 
##         killed     girlfriend       officers         friday          three 
##              5              5              5              4              4 
##       domestic           sons        morning        victims         inside 
##              4              3              3              3              3 
##          house       daughter        shooter       standoff         gunman 
##              3              3              3              3              3 
##   girlfriend's          young       children        suicide      suspected 
##              3              2              2              2              2 
##       shooting         family         former           fire      following 
##              2              2              2              2              2 
##       military        dispute           kill        officer         walked 
##              2              2              2              2              2 
##        outside          front          holed           dead         others 
##              2              2              2              2              2 
##       violence         sister      boyfriend      apartment          first 
##              2              2              2              2              2 
##          floor          lived         people        wounded       incident 
##              2              2              2              2              2 
##     committing    explanation        offered              s      initially 
##              1              1              1              1              1 
##      residence        shotgun        husband        murders        injures 
##              1              1              1              1              1 
##          rural        alabama        commits           boys         turned 
##              1              1              1              1              1 
##            gun        soldier        fatally            set        killing 
##              1              1              1              1              1 
##       suffered post-traumatic         stress       disorder         career 
##              1              1              1              1              1 
##       included          stint           iraq            led          shoot 
##              1              1              1              1              1 
##         injure      bystander         trying           help         around 
##              1              1              1              1              1 
##            9am         phoned          mason         county      sheriff's 
##              1              1              1              1              1 
##         office            say          going           next        flushed 
##              1              1              1              1              1 
##           swat           team          fired           tear            gas 
##              1              1              1              1              1
# Establecemos una semilla para la generación de números aleatorios
set.seed(100)

# Creamos un wordcloud con la matriz de términos por documento
textplot_wordcloud(dfm_tiroteos_domestic_dispute, 
                   min_count = 2,    # Solo mostramos palabras con al menos 2 ocurrencias
                   random_order = FALSE, # Colocamos las palabras más frecuentes en el centro
                   rotation = 0,         # Las palabras se mostrarán sin rotación (horizontalmente)
                   color = RColorBrewer::brewer.pal(8,"Spectral")) # Usamos una paleta de colores predefinida

Análisis de la nube de palabras para la causa “domestic dispute”:

La nube de palabras para la causa “disputa doméstica” revela un patrón de eventos altamente personales y frecuentemente letales, con un impacto significativo en el entorno familiar. La prominencia de términos como “wife”, “girlfriend”, “home”, y “officers” subraya la naturaleza íntima de estos conflictos y la frecuente intervención policial. Palabras como “shot” y “killed” destacan la violencia y las consecuencias fatales de estos incidentes. La presencia de términos relacionados con familiares, como “children” y “sons”, indica que estos tiroteos afectan a múltiples miembros de la familia, no solo a las parejas directamente involucradas.

Ahora, generamos un nuevo worldcloud con bigramas para comparar con el anterior:

# Ahora probamos a hacerlo con bigramas
tok_tiroteos_domestic_dispute_2 <- tokens_ngrams(tok_tiroteos_domestic_dispute, n = 2)

# Creamos una matriz de DFM a partir de los tokens
# Esta matriz cuenta la frecuencia de cada término
dfm_tiroteos_domestic_disputeo_2 <- dfm(tok_tiroteos_domestic_dispute_2)

# Extraemos las 20 palabras más frecuentes de la DFM
topfeatures(dfm_tiroteos_domestic_disputeo_2, 20)
##            wife_two         killed_wife       man_suspected            two_sons 
##                   5                   3                   2                   2 
##      friday_morning       three_victims         shot_killed    domestic_dispute 
##                   2                   2                   2                   2 
##          dead_three        three_others     shot_girlfriend   domestic_violence 
##                   2                   2                   2                   2 
## girlfriend's_sister    sister_boyfriend         first_floor          man_killed 
##                   2                   2                   2                   1 
##           two_young      young_children children_committing  committing_suicide 
##                   1                   1                   1                   1
# Extraemos las 100 palabras más frecuentes de la DFM para un análisis más detallado
topfeatures(dfm_tiroteos_domestic_disputeo_2, 100)
##                wife_two             killed_wife           man_suspected 
##                       5                       3                       2 
##                two_sons          friday_morning           three_victims 
##                       2                       2                       2 
##             shot_killed        domestic_dispute              dead_three 
##                       2                       2                       2 
##            three_others         shot_girlfriend       domestic_violence 
##                       2                       2                       2 
##     girlfriend's_sister        sister_boyfriend             first_floor 
##                       2                       2                       2 
##              man_killed               two_young          young_children 
##                       1                       1                       1 
##     children_committing      committing_suicide     suicide_explanation 
##                       1                       1                       1 
##     explanation_offered      suspected_shooting           shooting_wife 
##                       1                       1                       1 
##             sons_friday          morning_family                family_s 
##                       1                       1                       1 
##                  s_home              home_three       victims_initially 
##                       1                       1                       1 
##          initially_shot             shot_inside        inside_residence 
##                       1                       1                       1 
##       residence_shotgun         husband_murders            murders_wife 
##                       1                       1                       1 
##            wife_injures             injures_two               sons_home 
##                       1                       1                       1 
##              home_rural           rural_alabama         alabama_commits 
##                       1                       1                       1 
##         commits_suicide                man_shot                two_boys 
##                       1                       1                       1 
##             boys_turned              turned_gun          former_soldier 
##                       1                       1                       1 
##         soldier_fatally            fatally_shot               shot_wife 
##                       1                       1                       1 
##            two_children            children_set               set_house 
##                       1                       1                       1 
##              house_fire            fire_killing        killing_suffered 
##                       1                       1                       1 
## suffered_post-traumatic   post-traumatic_stress         stress_disorder 
##                       1                       1                       1 
##      disorder_following      following_military         military_career 
##                       1                       1                       1 
##         career_included          included_stint              stint_iraq 
##                       1                       1                       1 
##             dispute_led                 led_man               man_shoot 
##                       1                       1                       1 
##        shoot_girlfriend         girlfriend_kill           kill_daughter 
##                       1                       1                       1 
##         daughter_injure        injure_bystander        bystander_trying 
##                       1                       1                       1 
##             trying_help         help_girlfriend              around_9am 
##                       1                       1                       1 
##              9am_friday             morning_man              man_phoned 
##                       1                       1                       1 
##          phoned_officer           officer_mason            mason_county 
##                       1                       1                       1 
##        county_sheriff's        sheriff's_office              office_say 
##                       1                       1                       1 
##                say_shot             shot_family            family_going 
##                       1                       1                       1 
##              going_kill               kill_next                next_man 
##                       1                       1                       1 
##             man_flushed           flushed_house              house_swat 
##                       1                       1                       1 
##               swat_team              team_fired              fired_tear 
##                       1                       1                       1 
##                tear_gas 
##                       1
# Establecemos una semilla para la generación de números aleatorios
set.seed(100)

# Creamos un wordcloud con la matriz de términos por documento
textplot_wordcloud(dfm_tiroteos_domestic_disputeo_2, 
                   min_count = 1,    # Solo mostramos palabras con al menos 1 ocurrencias
                   random_order = FALSE, # Colocamos las palabras más frecuentes en el centro
                   rotation = 0,         # Las palabras se mostrarán sin rotación (horizontalmente)
                   color = RColorBrewer::brewer.pal(8,"Spectral")) # Usamos una paleta de colores predefinida

La nube de palabras con bigramas para la causa “disputa doméstica” revela un patrón de eventos personales y letales, con un impacto significativo en el entorno familiar. Bigrams como “wife two”, “two sons”, y “domestic violence” destacan la naturaleza íntima de estos conflictos y la frecuencia con que afectan a los miembros de la familia, especialmente a esposas e hijos. La presencia de términos relacionados con disparos y muertes como “shot killed” y “committing suicide” subraya la violencia y las consecuencias fatales de estos incidentes.

TidyText

Usamos tidytext porque es una biblioteca flexible en R diseñada específicamente para manipular y analizar datos textuales en formato “tidy”. Para este estudio, he decidido hacer el análisis de texto para los datos comparando la columna Gender entre Male y Female, sin tener en cuenta aquellos con valor Unknown.

# Verificamos las primeras filas del dataset
head(datos_limpios)
# Filtramos los datos para incluir solo los generos 'Male' y 'Female'
datos_filtrados_genero <- datos_limpios %>%
  filter(Gender %in% c("Male", "Female"))

# Verificamos el número de filas en el nuevo conjunto de datos
nrow(datos_filtrados_genero)
## [1] 297

Creamos un histograma utilizando ggplot2 para la visualizacion por fechas:

ggplot(datos_filtrados_genero, 
       aes(x = Date, 
           fill = Gender)) +
  geom_histogram(position = "identity", 
                 bins = 20, 
                 show.legend = FALSE) +
  facet_wrap(~Gender, ncol = 1) +
  
  # Agregamos etiquetas al gráfico
  labs(title = "Histograma de Tiroteos por Género",
       x = "Fecha del Titoteo",
       y = "Frecuencia")

Ahora, hacemos una representacion sin histograma:

datos_filtrados_genero %>%
  group_by(Gender, Date) %>%
  add_count() %>%
  ggplot(aes(x = Date, 
             color = Gender, y=n)) +
  geom_line() +
  facet_wrap(~ Gender, ncol = 1)

En ambos gráficos de tiroteos por género podemos observar una marcada desigualdad entre hombres y mujeres en la realización de estos actos. Los tiroteos cometidos por mujeres son esporádicos y significativamente menos frecuentes, con algunos eventos aislados desde 1980 hasta 2017. En contraste, los tiroteos cometidos por hombres son mucho más numerosos y muestran una tendencia creciente, especialmente a partir del año 2000, con un notable aumento alrededor de 2015.

Vamos a centrarnos en el periodo de 2010 a 2017:

# Definimos el rango de fechas desde 2010 hasta 2017
fecha_inicio <- as.Date("2010-01-01")
fecha_fin <- as.Date("2017-12-31")

# Filtramos los datos para incluir solo tiroteos desde 2010 hasta 2017 y de los generos especificados
datos_filtrados_genero_2010_2017 <- datos_limpios %>%
  filter(Date >= fecha_inicio & Date <= fecha_fin
         & (Gender %in% c("Male", "Female")))

# Verificamos el número de filas en el nuevo conjunto de datos
nrow(datos_filtrados_genero_2010_2017)
## [1] 174

Creamos un histograma utilizando ggplot2 para la visualizacion por fechas:

ggplot(datos_filtrados_genero_2010_2017, 
       aes(x = Date, 
           fill = Gender)) +
  geom_histogram(position = "identity", 
                 bins = 20, 
                 show.legend = FALSE) +
  facet_wrap(~Gender, ncol = 1) +
  
  # Agregamos etiquetas al gráfico
  labs(title = "Histograma de Tiroteos por Género (2010-2017)",
       x = "Fecha del Titoteo",
       y = "Frecuencia")

Ahora, hacemos una representacion sin histograma:

datos_filtrados_genero_2010_2017 %>%
  group_by(Gender, Date) %>%
  add_count() %>%
  ggplot(aes(x = Date, 
             color = Gender, y=n)) +
  geom_line() +
  facet_wrap(~ Gender, ncol = 1)

El gráfico vuelve a indicar que los tiroteos cometidos por hombres son más comunes y muestran un aumento significativo en 2016, posiblemente relacionado con tiroteos como el del Club Pulse, en Orlando, Florida, el 12 de junio de 2016, que ya ha sido mencionado varias veces durante el trabajo ya que hubo un gran número de víctimas. En contraste, los tiroteos cometidos por mujeres son extremadamente raros en el período analizado, y además solo hay hasta 2014.

# Definimos un patrón regex para remover entidades HTML
remove_reg <- "&[^\\s]*;"

# Hacemos un proceso de limpieza con el dataframe 'datos_filtrados_genero'
tidy_datos_genero <- datos_filtrados_genero %>% 
  mutate(text = str_remove_all(Summary, remove_reg)) %>%  # Removemos entidades HTML definidas en 'remove_reg' de la columna 'Summary'
  mutate(text = str_remove_all(text, "^[@#]\\w+"))  # Removemos caracteres especiales

# Mostramos el número de filas del dataframe después de la limpieza
nrow(tidy_datos_genero)
## [1] 297
# Convertimos el texto de los resúmenes en palabras individuales y removemos stopwords
tidy_datos_filtrados_genero <- tidy_datos_genero %>%
  unnest_tokens(word, text) %>%  # Descomponemos el texto en palabras individuales
  filter(
    !word %in% stop_words$word,  # Excluimos palabras que son stopwords
    !word %in% str_remove_all(stop_words$word, "'"),  # Excluimos stopwords después de remover apóstrofes
    str_detect(word, "[a-z]")  # Conservamos solo palabras que contengan letras minúsculas
  )

# Verificar las primeras filas de los tokens
head(tidy_datos_filtrados_genero)
# Calculamos la frecuencia de palabras por género y ajustamos por el total de palabras
frequency_genero <- tidy_datos_filtrados_genero %>% 
  count(Gender, word, sort = TRUE) %>%  # Contamos y ordenamos las palabras por frecuencia dentro de cada genero
  left_join(
    tidy_datos_filtrados_genero %>% 
      count(Gender, name = "total")  # Calculamos el total de palabras por genero
  ) %>%
  mutate(freq = n / total)  # Calculamos la frecuencia relativa de cada palabra (número de veces que aparece / total de palabras en el genero)

# Mostramos los resultados obtenidos
View(frequency_genero)

Comparación de tokens Male versus Female:

# Filtrar 'frequency_genero' para incluir solo las categorías 'Male' y 'Female'
frequency_para_plot <- frequency_genero[frequency_genero$Gender 
                                        %in% c("Male", "Female"),] 

# Reorganizar el dataframe para comparar las frecuencias de palabras entre géneros
frequency_para_plot <- frequency_para_plot %>% 
  select(Gender, word, freq) %>%  # Seleccionamos solo las columnas necesarias
  pivot_wider(names_from = Gender,  # Convertimos de formato largo a ancho
              values_from = freq) %>%
  arrange(Male, Female)  # Ordenamos el dataframe por 'Male' y luego 'Female'

# Calcular y mostrar el percentil 50 (mediana) de la frecuencia de 'Male' multiplicado por 10 millones
quantile(frequency_para_plot$Male,
         probs = c(0.5),
         na.rm = TRUE) * 10000000
##      50% 
## 1685.204
# Crear un gráfico de dispersión con texto para cada punto
ggplot(frequency_para_plot, 
       aes(Male, Female)) +
  geom_jitter(alpha = 0.1, size = 2.5, width = 0.25, height = 0.25) +  # Añadimos puntos con 'jitter'
  geom_text(aes(label = word), check_overlap = TRUE, vjust = 1.5) +  # Añadimos texto a cada punto
  scale_x_log10(labels = percent_format(), limits = c(0.001, 0.1)) +  # Escalamos el eje X en logaritmo y ajustamos los límites
  scale_y_log10(labels = percent_format(), limits = c(0.001, 0.1)) +  # Escalamos el eje Y en logaritmo y ajustamos los límites
  geom_abline(intercept = 0, slope = 1, color = "red") +  # Añadimos una línea roja diagonal
  labs(title = "Comparación de Frecuencia de Palabras entre Géneros",
       x = "Frecuencia en Male",
       y = "Frecuencia en Female")

El gráfico muestra una comparativa entre las frecuencias de términos utilizados por los géneros del tirador ‘Male’ y ‘Female’. Palabras como “children” y “worker” son más comunes en resúmenes de tiroteos realizados por mujeres, lo que puede indicar que estos incidentes están más relacionados con temas personales. Sin embargo, palabras como “police”, “killed” y “fire” son más frecuentes en resúmenes de tiroteos realizados por hombres, lo que nos indica que son tiroteos sin una causa específica ni más personal como en el caso de las mujeres. Algunas palabras, como “students” tienen un uso más equilibrado entre ambos géneros.

# Cálculo de log odds ratio para las palabras entre las dos categorías de género
word_ratios_genero <- tidy_datos_filtrados_genero %>%
  filter(Gender %in% c("Male", "Female")) %>%
  count(word, Gender) %>%  # Contamos las apariciones de cada palabra por género
  group_by(word) %>%
  filter(n >= 1) %>%  # Filtramos palabras que aparecen al menos 1 vez
  ungroup() %>%
  pivot_wider(names_from = Gender,  # Convertimos de formato largo a ancho
              values_from = n, 
              values_fill = 0) %>%
  mutate_if(is.numeric, list(~(. + 1) / (sum(.) + 1))) %>%  # Smoothing
  mutate(logratio = log(Male / Female)) %>%  # Calculamos el log odds ratio
  arrange(desc(logratio))  # Ordenamos por log odds ratio


# Visualización del log odds ratio
word_ratios_genero %>%
  group_by(logratio < 0) %>%
  slice_max(abs(logratio), n = 20) %>%  # Seleccionamos los 20 valores más altos de log odds ratio
  ungroup() %>%
  mutate(word = reorder(word, logratio)) %>%
  ggplot(aes(word, logratio, fill = logratio < 0)) +
  geom_col(show.legend = TRUE) +
  coord_flip() +  # Invertimos los ejes para mejor visualización
  ylab("log odds ratio (Male/Female)") +  # Etiqueta para el eje Y
  scale_fill_discrete(name = "", labels = c("Male", "Female")) +  # Colores distintos para cada lado del género
  labs(title = "Log Odds Ratio de Palabras entre Géneros")

El gráfico de log odds ratio muestra las palabras que son significativamente más frecuentes en descripciones de tiroteos cometidos por hombres (Male) en comparación con mujeres (Female) y viceversa. Las barras en color rojo representan palabras más asociadas con hombres, mientras que las barras en color azul representan palabras más asociadas con mujeres.

Las palabras como “shot”, “people”, “wounded” y “student” son más frecuentes en los tiroteos cometidos por hombres. Por otro lado, las palabras como “woods”, “tribe”, “bathroom” y “psychological” son más frecuentes en los tiroteos cometidos por mujeres.

Este análisis sugiere diferencias en los contextos de los incidentes entre géneros, con los hombres más asociados con términos directamente relacionados con el acto de disparar y las víctimas, mientras que las mujeres están más asociadas con términos que pueden indicar entornos o motivos.

Topic Model

# Filtrar datos para aquellos con problemas de salud mental
datos_mental_health <- datos_limpios %>% filter(Mental.Health.Issues == "Yes")

# Creamos un corpus con el contenido de los resúmenes filtrados
mental_health_corpus <- corpus(datos_mental_health$Summary)

# Tokenizamos el corpus, es decir, dividimos el texto en palabras o símbolos individuales
# Además, eliminamos números, puntuación, símbolos, espacios adicionales, menciones de Twitter y URLs
# para limpiar los datos antes del análisis
mental_health_tokens <- tokens(mental_health_corpus,
                         remove_numbers = TRUE,
                         remove_punct = TRUE,
                         remove_symbols = TRUE,
                         remove_separators = TRUE,
                         remove_twitter = TRUE,
                         remove_url = TRUE)

# Definimos las stopwords en inglés
english_stopwords <- stopwords("english")

# Añadimos números y signos de puntuación a la lista de stopwords
my_stopwords2 <- c(english_stopwords,
                   as.character(c(0:9)),  # Números del 0 al 9
                   "|", "&", "$")  # Signos de puntuación y símbolos monetarios

# Añadimos palabras y símbolos específicos que no queremos incluir en el análisis
stop_def <- c(my_stopwords2,
              "two",
              "three",
              "four",
              "shooting", 
              "shooter", 
              "victim", 
              "killed", 
              "injured", 
              "people", 
              "killing", 
              "shot")

# Eliminamos palabras no deseadas (stopwords y otras definidas) de los tokens
mental_health_palabras <- tokens_select(mental_health_tokens,
                                pattern = stop_def,
                                selection = "remove")

Creamos la DFM y el modelo LDA:

# Creamos una matriz de términos por documento (DFM) 
dfm_mental_health <- dfm(mental_health_palabras)

# Tomamos el tiempo antes de comenzar a ajustar el modelo de tópicos
time1 <- Sys.time()

# Preparamos la DFM para el modelado, eliminando términos poco frecuentes
quant_dfm_1 <- dfm_trim(dfm_mental_health, min_termfreq = 10)

# Establecemos una semilla para reproducibilidad y verificamos si el paquete 'topicmodels' está instalado
set.seed(100)
if (require(topicmodels)) {
  # Ajustamos un modelo LDA con 10 tópicos a la DFM
  lda_fit_1 <- LDA(convert(quant_dfm_1, to = "topicmodels"), 
                 k = 10)
  # Extraemos los 6 términos más representativos de cada tópico
  get_terms(lda_fit_1, 6)
}
##      Topic 1    Topic 2    Topic 3   Topic 4    Topic 5      Topic 6  
## [1,] "students" "later"    "former"  "school"   "former"     "police" 
## [2,] "student"  "opened"   "police"  "suicide"  "office"     "wounded"
## [3,] "others"   "arrested" "entered" "students" "old"        "fire"   
## [4,] "wounded"  "home"     "wife"    "opened"   "fire"       "student"
## [5,] "school"   "another"  "fire"    "injuring" "california" "others" 
## [6,] "began"    "police"   "old"     "old"      "man"        "man"    
##      Topic 7   Topic 8   Topic 9    Topic 10  
## [1,] "student" "others"  "school"   "police"  
## [2,] "opened"  "student" "high"     "opened"  
## [3,] "police"  "home"    "arrested" "fire"    
## [4,] "man"     "later"   "others"   "school"  
## [5,] "another" "five"    "injuring" "arrested"
## [6,] "entered" "fire"    "teacher"  "old"
# Tomamos el tiempo después de ajustar el modelo de tópicos
time2 <- Sys.time()

# Imprimimos la duración del ajuste del modelo
print(time2-time1)
## Time difference of 0.151973 secs
# Guardamos el modelo de tópicos para uso futuro
save(lda_fit_1, file = "mental_health_topicmodel_1.rda")
# 'lda_fit_1' es el objeto LDA y 'quant_dfm_1' es el DFM
# Primero obtenemos la matriz de términos por tópico
kk_1 <- lda_fit_1@beta

# Los nombres de las palabras tiene que estar en las columnas
colnames(kk_1) <- lda_fit_1@terms

# Definimos el layout para múltiples gráficos
par(mfrow=c(3, 2))

# Iteramos a través de cada tópico
for (k in 1:6) {  # Visualizamos 6 topicos
  
  # Extraemos las probabilidades para el tópico k
  topic_probabilities_1 <- kk_1[k, ]
  
  # Convertimos a dataframe para manipulación
  topic_df_1 <- data.frame(word = names(topic_probabilities_1), rank = rank(topic_probabilities_1))
  
  # Ordenamos las palabras por su probabilidad dentro del tópico
  topic_df_1 <- topic_df_1[order(-topic_df_1$rank), ]
  
  # Ajustamos los valores para visualización
  topic_df_1$freq <- topic_df_1$rank - max(topic_df_1$rank) + 100
  
  # Escogemos una paleta de colores
  pal <- brewer.pal(8, "Dark2")
  
  # Generamos la nube de palabras para el tópico k
  wordcloud(words = topic_df_1$word, freq = topic_df_1$freq, 
            scale = c(1.5, 0.05),
            max.words = 200, 
            random.order = FALSE, 
            rot.per = 0, 
            colors = pal,
            random.clor = TRUE)
  
  # Añadimos un título al gráfico
  title(main = paste("Topic", k), col.main = "black", font = 10)
}

Topic 1

  • Palabras clave: children, student, campus, california, entered, home, former, wounded, officer, committing, suicide.

  • Interpretación: Este tópico se centra en incidentes que ocurren en entornos educativos, como campus y escuelas. También menciona a niños y estudiantes, así como a términos relacionados con la policía y suicidios, sugiriendo que estos incidentes afectan a menores y ocurren en instituciones educativas, posiblemente involucrando respuestas policiales y situaciones de suicidio.

Topic 2

  • Palabras clave: campus, teacher, students, entered, wounded, school, committing, suicide, california, police, officer.

  • Interpretación: Similar al Tópico 1, este tema también se enfoca en entornos educativos, destacando la participación de maestros y estudiantes. Las palabras “wounded” y “committing suicide” indican que estos incidentes resultan en heridos y suicidios.

Topic 3

  • Palabras clave: wounding, scene, teacher, police, former, committing, suicide, campus, children, office, gun.

  • Interpretación: Este tópico resalta incidentes que involucran heridas, situaciones en oficinas y escenarios de suicidio, sugiriendo que los incidentes pueden ocurrir en lugares de trabajo o involucrar a individuos que enfrentan problemas de salud mental.

Topic 4

  • Palabras clave: home, wounded, school, students, police, teacher, committing, suicide, high, campus.

  • Interpretación: Este tema incluye referencias tanto a entornos educativos como al hogar, indicando que los tiroteos pueden ocurrir en ambos lugares. La mención de “police” y “wounded” sugiere la intervención de las fuerzas del orden y la presencia de heridos.

Topic 5

  • Palabras clave: suicide, former, students, office, committing, home, scene, california, teacher.

  • Interpretación: Este tópico también menciona lugares de trabajo y escenarios domésticos, con un enfoque en incidentes que resultan en suicidios. La repetición de términos como “students” y “teacher” sugiere la implicación de entornos educativos.

Topic 6

  • Palabras clave: wounded, officer, home, school, committing, suicide, students, high, campus, teacher.

  • Interpretación: Este tema combina elementos de entornos educativos y domésticos, con un énfasis en heridos y suicidios. La mención de “officer” indica la intervención policial en estos incidentes.

Con el análisis de los tópicos, podemos ver que los tiroteos relacionados con problemas de salud mental ocurren en una variedad de escenarios, principalmente en entornos educativos, domésticos y laborales.

La frecuencia de términos como “school”, “students”, “teacher”, “campus” y “high school” indica que las instituciones educativas son un escenario común para los tiroteos relacionados con problemas de salud mental, resaltando la gran tasa de suicidios de persona jóvenes. También encontramos varias veces la palabra “former”, que nos indica que estos problemas de salud mental pudieron estar causados por despidos.

Sentiment Analysis

Sentiment Analysis General

Vamos a hacer un primer sentiment analysis general, sin centrarnos en ningún estado ni causa.

Usamos el léxico bing para el análisis de sentimientos. El léxico Bing es un conjunto de palabras clasificadas según su polaridad de sentimiento.

# Extraemos solo el contenido de texto de los resúmenes para análisis de sentimiento
text_summary <- datos_limpios %>%
  select(Summary)

# Transformamos el texto de los resúmenes en un formato 'tidy', dividiéndolo en palabras individuales
tidy_text_summary <- text_summary %>%
  unnest_tokens(word, Summary)

# Unimos nuestro conjunto de datos de texto con un léxico de sentimientos (usamos el léxico 'bing') y contamos la ocurrencia de sentimientos
sentiment_analysis_summary <- tidy_text_summary %>%
  inner_join(get_sentiments("bing"), by = "word") %>%
  count(sentiment, sort = TRUE)

# Graficamos los resultados del análisis de sentimientos para visualizar el balance entre sentimientos positivos y negativos
sentiment_analysis_summary %>%
  ggplot(aes(x = sentiment, y = n, fill = sentiment)) +
  geom_bar(stat = "identity") +
  ggtitle("Análisis de Sentimientos de los Resúmenes de Tiroteos")

# Imprimimos en consola el balance de sentimientos
print(sentiment_analysis_summary)
##   sentiment   n
## 1  negative 655
## 2  positive  71

Como era de esperar, el sentimiento general de estos textos es negativo, ya que se trata de tiroteos.

Ahora usamos el paquete SentimentAnalysis:

# Seleccionamos una muestra aleatoria de 1000 resúmenes para un análisis más detallado
set.seed(123) # Para reproducibilidad
example_summary <- sample(datos_limpios$Summary, 100)

# Creamos histogramas para visualizar la distribución de los sentimientos utilizando diferentes diccionarios de análisis de sentimiento.

sentiment_summary <- analyzeSentiment(example_summary)

# Realizamos un análisis de sentimiento con el diccionario QDAP y resumimos los resultados
sentiment_summary_qdap <- summary(sentiment_summary$SentimentQDAP, na.rm = TRUE)
hist(sentiment_summary$SentimentQDAP, breaks = 50, main = "Distribución del Sentimiento QDAP", xlab = "Sentimiento")

# Diccionario Harvard IV-4 (HE)
sentiment_summary_he <- summary(sentiment_summary$SentimentHE)
hist(sentiment_summary$SentimentHE, breaks = 50, main = "Distribución del Sentimiento Harvard IV-4", xlab = "Sentimiento")

# Diccionario General Inquirer (GI)
sentiment_summary_gi <- summary(sentiment_summary$SentimentGI)
hist(sentiment_summary$SentimentGI, breaks = 50, main = "Distribución del Sentimiento General Inquirer", xlab = "Sentimiento")

Otra vez, podemos observar que predominan los sentimiento negativos. Por ejemplo, tenemos un gran pico justo a la izquierda del 0.0 con los tres diccionarios, y los diccionarios QDAP y GI presentan muchas barras a lo largo de los valores negativos.

Ahora usamos AFINN, un léxico de análisis de sentimientos AFINN es popular para el análisis de sentimientos en textos cortos:

# Creamos un conjunto de datos 'tidy' dividiendo los textos en palabras individuales
datos_tidy_summary <- datos_limpios %>% 
  unnest_tokens(word, Summary)

# Realizamos un análisis de sentimiento utilizando el diccionario 'afinn'
datos_sentiment_summary <- datos_tidy_summary %>% 
  inner_join(get_sentiments("afinn"), by = "word") %>%
  inner_join(datos_limpios %>% select(S., Summary), by = "S.") %>%
  group_by(S.) %>% 
  summarise(sentiment = sum(value), text = first(Summary))

# Ordenamos los resultados del análisis de sentimientos
datos_sentiment_summary_sorted <- datos_sentiment_summary %>% 
  arrange(desc(sentiment))

# Graficamos la distribución del sentimiento en los resumenes
ggplot(data = datos_sentiment_summary_sorted, aes(x = sentiment)) + 
  geom_histogram(bins = 50, color = 'firebrick', fill = 'darkred') + 
  scale_x_continuous(breaks = seq(min(datos_sentiment_summary_sorted$sentiment), max(datos_sentiment_summary_sorted$sentiment), by = 5)) +
  xlab("Sentimiento") + 
  ylab("Cantidad de Resúmenes") + 
  ggtitle("Distribución de Sentimiento en Resúmenes de Tiroteos")

La mayoría de los resúmenes tienen un tono negativo, con picos significativos alrededor de los valores de -5, -6 y -8, lo que indica una alta frecuencia de términos negativos. Los sentimientos negativos alcanzan un valor mínimo de aproximadamente -21, sugiriendo que algunos resúmenes contienen una fuerte carga de términos negativos.

Voy a analizar los resúmenes de los tiroteos correspondientes a los sentimientos -20 y -21, que son con diferencias los más negativos:

# Resumen del tiroteo con sentimiento -21
resumen_negative21 <- datos_sentiment_summary_sorted %>%
  filter(sentiment == -21)

print(resumen_negative21)
## # A tibble: 1 × 3
##      S. sentiment text                                                          
##   <int>     <dbl> <chr>                                                         
## 1   207       -21 on march 10, 2009, a 28-year-old grocery worker in geneva cou…

Ocurrió el 10 de marzo de 2009, en el condado de Geneva, Alabama. Un trabajador de 28 años mató a su madre, incendió su casa, y luego asesinó a su tío, primos, vecinos, y abuela. Continuó matando a tres transeúntes y disparó a cualquiera que intentara escapar o detenerlo. En total, diez personas fueron asesinadas y seis resultaron heridas antes de que el perpetrador se suicidara cuando la policía intentó detenerlo. Este resumen es extremadamente negativo debido a la brutalidad y el número de víctimas, incluyendo a miembros de su propia familia y personas inocentes.

# Resumen del tiroteo con sentimiento -20
resumen_negative20 <- datos_sentiment_summary_sorted %>%
  filter(sentiment == -20)

print(resumen_negative20)
## # A tibble: 1 × 3
##      S. sentiment text                                                          
##   <int>     <dbl> <chr>                                                         
## 1     9       -20 kori ali muhammad, 39, opened fire along a street in downtown…

Kori Ali Muhammad, de 39 años, disparó al azar y mató a tres personas en una calle de Fresno, California, en un presunto crimen de odio. Muhammad, quien es afroamericano, mató a tres víctimas blancas y describió su ataque como motivado racialmente. También gritó “allahu akbar” al ser arrestado, aunque no se encontraron vínculos con el terrorismo islamista. Este resumen es extremadamente negativo debido a la motivación racial del crimen y el asesinato aleatorio de personas inocentes.

También me ha parecido interesante estudiar el resumen que aparece en el gráfico con un sentimiento positivo de 3, porque me parece imposible que haya algún resumen de un tiroteo que sea positivo o que tenga algo de contenido positivo:

# Resumen del tiroteo con sentimiento 3
resumen_positive3 <- datos_sentiment_summary_sorted %>%
  filter(sentiment == 3)

print(resumen_positive3)
## # A tibble: 1 × 3
##      S. sentiment text                                                          
##   <int>     <dbl> <chr>                                                         
## 1   165         3 on august 14, 2013, a 40-year old man obsessed with actress s…

El texto describe un incidente trágico y violento que ocurrió el 14 de agosto de 2013, donde un hombre de 40 años, obsesionado con la actriz Selena Gomez, disparó fatalmente a su madre, hermana, sobrina y sobrino recién nacido. Posteriormente fue arrestado y declaró a la policía que necesitaba alejarse de su familia porque ellos le impedían estar con su “verdadero amor”.

La razón del sentimiento positivo de este resumen puede deberse a que palabras como “amor verdadero” o “Selena Gómez” podrían ser interpretadas positivamente en un análisis superficial, sin considerar que en este contexto específico refieren a una obsesión patológica.

Probamos a usar quanteda para el análisis de sentimiento:

# Verificamos el tamaño del dataset
print(paste("Número de resúmenes disponibles:", nrow(datos_limpios)))
## [1] "Número de resúmenes disponibles: 323"
# Ajustamos el tamaño de la muestra según el tamaño del dataset
# Tomamos el mínimo entre 1000 y el tamaño del dataset
tamaño_muestra <- min(1000, nrow(datos_limpios))  

# Seleccionamos una muestra aleatoria de resúmenes
set.seed(123) # Para reproducibilidad
example_summary <- sample(datos_limpios$Summary, tamaño_muestra)

# Creamos un corpus a partir de los resúmenes y añadimos la fecha de publicación como metadato
summary_corpus <- corpus(datos_limpios$Summary)
docvars(summary_corpus, "Fecha") <- datos_limpios$Date

# Tokenizamos el corpus
tokens_summary <- tokens(summary_corpus)

# Aplicamos un diccionario para identificar términos relacionados con sentimientos
tokens_summary_sentiment <- tokens_lookup(tokens_summary, dictionary = data_dictionary_LSD2015[1:2])

# Agrupamos los términos por día y creaMOS una matriz de términos por fecha
dfmat_summary_sentiment <- dfm(tokens_summary_sentiment) %>% 
  dfm_group(groups = docvars(summary_corpus, "Fecha"))

# Representamos visualmente la frecuencia de términos positivos y negativos a lo largo del tiempo
matplot(dfmat_summary_sentiment$Fecha, 
        dfmat_summary_sentiment, 
        type = "l", 
        lty = 1, 
        col = 1:2,
        ylab = "Frequency", 
        xlab = "")
grid()
legend("topleft", 
       col = 1:2, 
       legend = colnames(dfmat_summary_sentiment), 
       lty = 1, 
       bg = "white")

Podemos observar que hay un pico elevado entre 1990 y 1995, y luego una gran cantidad de sentimientos negativos a partir de 2010.

Vamos a analizar los textos en estos dos periodos de tiempo.

Sentiment Analysis 1990-1995

# Filtramos los tokens para obtener aquellos entre 1990 y 1995
tokens_summary_1990_1995 <- tokens_subset(tokens_summary, 
                                (Fecha >= as.Date("1990-01-01") &
                                 Fecha <= as.Date("1995-12-31")))

# Aplicamos un diccionario para identificar términos relacionados con sentimientos
tokens_summary_sentiment_1990_1995 <- tokens_lookup(tokens_summary_1990_1995, 
                              dictionary = data_dictionary_LSD2015[1:2])

# Agrupamos los términos por día y creamos una matriz de términos por fecha
dfmat_summary_sentiment_1990_1995 <- dfm(tokens_summary_sentiment_1990_1995) %>% 
  dfm_group(groups = Fecha)

# Representamos visualmente la frecuencia de términos positivos y negativos a lo largo del tiempo
matplot(dfmat_summary_sentiment_1990_1995$Fecha, 
        dfmat_summary_sentiment_1990_1995, 
        type = "l", 
        lty = 1, 
        col = 1:2,
        ylab = "Frequency", 
        xlab = "")
grid()
legend("topleft", 
       col = 1:2, 
       legend = colnames(dfmat_summary_sentiment_1990_1995), 
       lty = 1, 
       bg = "white")

El mayor pico de sentimiento negativo en este periodo ocurre en 1993.

Investigando estas fechas he encontrado varios sucesos que ocurrieron, aunque puede que el pico en la gráfica no tenga que ver con estos:

  1. Tiroteo en la sede de la CIA: El 25 de enero de 1993, Mir Aimal Kansi, un nacional paquistaní, disparó y mató a dos empleados de la CIA e hirió a otros tres en Langley, Virginia. Este ataque fue motivado por la ira de Kansi hacia las políticas de Estados Unidos en el Medio Oriente y su intervención en los países musulmanes. Después del ataque, Kansi huyó y fue capturado en 1997, posteriormente fue extraditado a los EE.UU., juzgado, y ejecutado en 2002.

  2. Masacre de 101 California Street: El 1 de julio de 1993, Gian Luigi Ferri abrió fuego en un edificio de oficinas en San Francisco, California, matando a ocho personas e hiriendo a seis más antes de suicidarse. Este tiroteo fue uno de los más mortales en la historia de la ciudad y generó un debate significativo sobre el control de armas.

  3. Masacre de Brown’s Chicken: El 8 de enero de 1993, en Palatine, Illinois, siete empleados de un restaurante de Brown’s Chicken fueron asesinados durante un robo. Este caso permaneció sin resolver durante casi una década hasta que dos hombres fueron arrestados y condenados.

  4. Tiroteo en el Long Island Rail Road: El 7 de diciembre de 1993, Colin Ferguson disparó a pasajeros en un tren de Long Island Rail Road, matando a seis personas e hiriendo a diecinueve. Este ataque fue un punto crucial en las discusiones sobre seguridad y armas en el transporte público.

Vamos a analizar este año en concreto:

# Filtramos datos para el año 1993
datos_1993 <- datos_limpios %>%
  filter(Date >= as.Date("1993-01-01") & Date <= as.Date("1993-12-31"))

# Verificamos el tamaño del dataset filtrado
print(paste("Número de resúmenes disponibles en 1993:", nrow(datos_1993)))
## [1] "Número de resúmenes disponibles en 1993: 9"
# Extraemos columnas relevantes
datos_1993_relevantes <- datos_1993 %>%
  select(Summary, Location, State, Total.victims, Fatalities, Injured)

# Mostramos un resumen de los datos extraídos
summary(datos_1993_relevantes)
##    Summary            Location            State           Total.victims   
##  Length:9           Length:9           Length:9           Min.   : 3.000  
##  Class :character   Class :character   Class :character   1st Qu.: 3.000  
##  Mode  :character   Mode  :character   Mode  :character   Median : 5.000  
##                                                           Mean   : 8.333  
##                                                           3rd Qu.:12.000  
##                                                           Max.   :25.000  
##    Fatalities       Injured      
##  Min.   :1.000   Min.   : 1.000  
##  1st Qu.:1.000   1st Qu.: 2.000  
##  Median :2.000   Median : 4.000  
##  Mean   :3.222   Mean   : 5.556  
##  3rd Qu.:4.000   3rd Qu.: 6.000  
##  Max.   :9.000   Max.   :19.000
# Visualizamos el número de víctimas, fatalidades y heridos por ciudad y estado
ggplot(datos_1993_relevantes, aes(x = Location, y = Total.victims, fill = State)) +
  geom_bar(stat = "identity") +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
  labs(title = "Número de Víctimas por Ciudad y Estado en 1993",
       x = "Ciudad",
       y = "Número de Víctimas")

ggplot(datos_1993_relevantes, aes(x = Location, y = Fatalities, fill = State)) +
  geom_bar(stat = "identity") +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
  labs(title = "Número de Fatalidades por Ciudad y Estado en 1993",
       x = "Ciudad",
       y = "Número de Fatalidades")

ggplot(datos_1993_relevantes, aes(x = Location, y = Injured, fill = State)) +
  geom_bar(stat = "identity") +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
  labs(title = "Número de Heridos por Ciudad y Estado en 1993",
       x = "Ciudad",
       y = "Número de Heridos")

Podemos ver que tanto en número de heridos como en número de víctimas, el más alto es en Nueva York.

# Filtramos datos para el año 1993 y el estado de Nueva York
datos_new_york_1993 <- datos_1993_relevantes %>%
  filter(State == "New York")

print(datos_new_york_1993)
##                                                                                                                                                                                                                                                                         Summary
## 1 on december 7, 1993, a 34-year-old mentally unstable man boarded a lond island rail road commuter train headed to nassau county in new york. he began shooting in the train car, killing six and wounding nineteen before being apprehended by three passengers in the train.
##                Location    State Total.victims Fatalities Injured
## 1 Garden City, New York New York            25          6      19

Podemos ver que el suceso es el último de los cuatro que hemos comentado arriba como posibilidades del pico de sentimiento negativo en este periodo de tiempo.

Sentiment Analysis 2010-2017

# Filtramos los tokens para obtener aquellos entre 2010 y 2017
tokens_summary_2010_2017 <- tokens_subset(tokens_summary, 
                                (Fecha >= as.Date("2010-01-01") &
                                 Fecha <= as.Date("2017-12-31")))

# Aplicamos un diccionario para identificar términos relacionados con sentimientos
tokens_summary_sentiment_2010_2017 <- tokens_lookup(tokens_summary_2010_2017, 
                              dictionary = data_dictionary_LSD2015[1:2])

# Agrupamos los términos por día y creamos una matriz de términos por fecha
dfmat_summary_sentiment_2010_2017 <- dfm(tokens_summary_sentiment_2010_2017) %>% 
  dfm_group(groups = Fecha)

# Representamos visualmente la frecuencia de términos positivos y negativos a lo largo del tiempo
matplot(dfmat_summary_sentiment_2010_2017$Fecha, 
        dfmat_summary_sentiment_2010_2017, 
        type = "l", 
        lty = 1, 
        col = 1:2,
        ylab = "Frequency", 
        xlab = "")
grid()
legend("topleft", 
       col = 1:2, 
       legend = colnames(dfmat_summary_sentiment_2010_2017), 
       lty = 1, 
       bg = "white")

El mayor pico de sentimiento negativo en este periodo ocurre en 2016.

Investigando estas fechas he encontrado varios sucesos que ocurrieron, aunque puede que el pico en la gráfica no tenga que ver con estos:

  1. Tiroteo en el Club Nocturno Pulse en Orlando: El 12 de junio de 2016, Omar Mateen, un ciudadano estadounidense de 29 años, perpetró un tiroteo masivo en el club nocturno Pulse en Orlando, Florida. Mateen mató a 49 personas e hirió a más de 50 antes de ser abatido por la policía. Este ataque fue el más mortífero en la historia de Estados Unidos hasta ese momento. Mateen, que había sido investigado previamente por el FBI por posibles vínculos con el terrorismo, realizó el ataque alegando lealtad al Estado Islámico.

  2. Tiroteo en el Centro Comercial Cascade: El 23 de septiembre de 2016, Arcan Cetin, un hombre de 20 años, llevó a cabo un tiroteo en el Cascade Mall en Burlington, Washington. Cetin mató a cinco personas antes de ser arrestado al día siguiente. Este incidente atrajo la atención nacional debido a la brutalidad del ataque y el posterior arresto del perpetrador, quien luego se suicidó en la cárcel mientras esperaba juicio.

Vamos a analizar este año en concreto:

# Filtramos datos para el año 2016
datos_2016 <- datos_limpios %>%
  filter(Date >= as.Date("2016-01-01") & Date <= as.Date("2016-12-31"))

# Verificamos el tamaño del dataset filtrado
print(paste("Número de resúmenes disponibles en 2016:", nrow(datos_2016)))
## [1] "Número de resúmenes disponibles en 2016: 69"
# Extraemos columnas relevantes
datos_2016_relevantes <- datos_2016 %>%
  select(Summary, Location, State, City, Total.victims, Fatalities, Injured)

# Mostramos un resumen de los datos extraídos
summary(datos_2016_relevantes)
##    Summary            Location            State               City          
##  Length:69          Length:69          Length:69          Length:69         
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##  Total.victims       Fatalities        Injured      
##  Min.   :  3.000   Min.   : 0.000   Min.   : 0.000  
##  1st Qu.:  4.000   1st Qu.: 1.000   1st Qu.: 2.000  
##  Median :  4.000   Median : 2.000   Median : 3.000  
##  Mean   :  6.594   Mean   : 2.797   Mean   : 4.014  
##  3rd Qu.:  5.000   3rd Qu.: 4.000   3rd Qu.: 4.000  
##  Max.   :102.000   Max.   :49.000   Max.   :53.000
# Visualizamos el número de víctimas, fatalidades y heridos por ciudad y estado
ggplot(datos_2016_relevantes, aes(x = Location, y = Total.victims, fill = State)) +
  geom_bar(stat = "identity") +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
  labs(title = "Número de Víctimas por Ciudad y Estado en 2016",
       x = "Ciudad",
       y = "Número de Víctimas")

ggplot(datos_2016_relevantes, aes(x = Location, y = Fatalities, fill = State)) +
  geom_bar(stat = "identity") +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
  labs(title = "Número de Fatalidades por Ciudad y Estado en 2016",
       x = "Ciudad",
       y = "Número de Fatalidades")

ggplot(datos_2016_relevantes, aes(x = Location, y = Injured, fill = State)) +
  geom_bar(stat = "identity") +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
  labs(title = "Número de Heridos por Ciudad y Estado en 2016",
       x = "Ciudad",
       y = "Número de Heridos")

Podemos ver que tanto en número de heridos como en número de víctimas como en número de fatalidades, el más alto es en Florida, en concreto en Orlando.

# Filtramos datos para el año 2016, el estado de Florida y la ciudad de Orlando
datos_florida_orlando_2016 <- datos_2016_relevantes %>%
  filter(State == "Florida" & City == "Orlando")

print(datos_florida_orlando_2016)
##                                                                                                                                                                           Summary
## 1 omar mateen, 29, attacked the pulse nighclub in orlando in the early morning hours of june 12. he was killed by law enforcement who raided the club after a prolonged standoff.
## 2                                                        two people were killed and several others were hurt in a mass shooting inside an orlando nightclub early sunday morning.
##           Location   State    City Total.victims Fatalities Injured
## 1 Orlando, Florida Florida Orlando           102         49      53
## 2 Orlando, Florida Florida Orlando            12          2      10

Podemos ver que el suceso es el primero que hemos comentado arriba como posibilidades del pico de sentimiento negativo en este periodo de tiempo.

Sentiment Analysis State

Vamos a hacer un segundo sentiment analysis por estado, usando para ello la columna State.

Usamos el léxico bing para el análisis de sentimientos. El léxico Bing es un conjunto de palabras clasificadas según su polaridad de sentimiento.

# Extraemos el contenido de texto de los resúmenes junto con los estados para el análisis de sentimiento
text_summary_state <- datos_limpios %>%
  select(State, Summary)

# Transformamos el texto de los resúmenes en un formato 'tidy', dividiéndolo en palabras individuales
tidy_text_summary_state <- text_summary_state %>%
  unnest_tokens(word, Summary)

# Unimos nuestro conjunto de datos de texto con un léxico de sentimientos (usamos el léxico 'bing') y contamos la ocurrencia de sentimientos
sentiment_analysis_summary_state  <- tidy_text_summary_state %>%
  inner_join(get_sentiments("bing"), by = "word") %>%
  count(sentiment, sort = TRUE)

# Graficamos los resultados del análisis de sentimientos para visualizar el balance entre sentimientos positivos y negativos
sentiment_analysis_summary_state %>%
  ggplot(aes(x = sentiment, y = n, fill = sentiment)) +
  geom_bar(stat = "identity") +
  ggtitle("Análisis de Sentimientos de los Resúmenes de Tiroteos por Estado")

# Imprimimos en consola el balance de sentimientos
print(sentiment_analysis_summary_state)
##   sentiment   n
## 1  negative 655
## 2  positive  71

Como era de esperar, el sentimiento general de estos textos también es negativo, ya que se trata de tiroteos por estado.

Vamos a ver un estudio por estado:

# Unimos nuestro conjunto de datos de texto con un léxico de sentimientos (usamos el léxico 'bing') y contamos la ocurrencia de sentimientos por estado
sentiment_analysis_summary_state <- tidy_text_summary_state %>%
  inner_join(get_sentiments("bing"), by = "word") %>%
  group_by(State, sentiment) %>%
  count() %>%
  spread(sentiment, n, fill = 0) %>%
  mutate(sentiment_score = positive - negative)

# Filtramos los estados 'NA'
sentiment_analysis_summary_state <- sentiment_analysis_summary_state %>%
  filter(!is.na(State))

# Graficamos los resultados del análisis de sentimientos para visualizar el balance entre sentimientos positivos y negativos por estado
ggplot(sentiment_analysis_summary_state, aes(x = reorder(State, sentiment_score), y = sentiment_score, fill = sentiment_score > 0)) +
  geom_bar(stat = "identity") +
  coord_flip() +
  labs(title = "Análisis de Sentimientos de los Resúmenes de Tiroteos por Estado", x = "Estado", y = "Puntuación de Sentimiento") +
  scale_fill_manual(values = c("red", "blue"), guide = FALSE)

# Imprimimos en consola el balance de sentimientos por estado
print(sentiment_analysis_summary_state)
## # A tibble: 55 × 4
## # Groups:   State [55]
##    State       negative positive sentiment_score
##    <chr>          <dbl>    <dbl>           <dbl>
##  1 Alabama           21        0             -21
##  2 Alaska             2        1              -1
##  3 Albuquerque        3        0              -3
##  4 Arizona           25        5             -20
##  5 Arkansas           1        1               0
##  6 CA                 4        0              -4
##  7 California        70        6             -64
##  8 CO                 3        0              -3
##  9 Colorado          17        0             -17
## 10 Connecticut        8        0              -8
## # ℹ 45 more rows

El análisis de sentimientos de los resúmenes de tiroteos por estado muestra una predominancia de sentimientos negativos en todos los estados. California y Washington destacan con puntuaciones de sentimiento particularmente bajas, lo que sugiere que los resúmenes de tiroteos en estos estados contienen una gran cantidad de términos con connotaciones negativas. En contraste, algunos estados como Maryland (MD), Iowa, y Arkansas muestran puntuaciones de sentimiento ligeramente menos negativas, aunque todavía predomina la negatividad. Estos resultados resaltan la carga emocional y la naturaleza trágica de los tiroteos, que inevitablemente llevan a resúmenes con sentimientos predominantemente negativos.

Ahora usamos el paquete SentimentAnalysis:

# Seleccionamos una muestra aleatoria de 1000 resúmenes para un análisis más detallado
set.seed(123) # Para reproducibilidad
example_data <- sample_n(datos_limpios, 100) # Selecciona una muestra de 100 registros incluyendo los estados

# Realizamos el análisis de sentimiento con el paquete SentimentAnalysis
sentiment_summary <- analyzeSentiment(example_data$Summary)

# Agregamos los resultados de sentimiento al dataframe original
example_data$SentimentQDAP <- sentiment_summary$SentimentQDAP
example_data$SentimentHE <- sentiment_summary$SentimentHE
example_data$SentimentGI <- sentiment_summary$SentimentGI

# Creamos histogramas para visualizar la distribución de los sentimientos utilizando diferentes diccionarios de análisis de sentimiento.

# Resumimos los resultados por estado para cada diccionario
sentiment_summary_qdap <- example_data %>%
  group_by(State) %>%
  summarise(mean_sentiment_qdap = mean(SentimentQDAP, na.rm = TRUE))

sentiment_summary_he <- example_data %>%
  group_by(State) %>%
  summarise(mean_sentiment_he = mean(SentimentHE, na.rm = TRUE))

sentiment_summary_gi <- example_data %>%
  group_by(State) %>%
  summarise(mean_sentiment_gi = mean(SentimentGI, na.rm = TRUE))

# Visualizamos los resultados para cada diccionario

# QDAP
ggplot(sentiment_summary_qdap, aes(x = reorder(State, mean_sentiment_qdap), y = mean_sentiment_qdap, fill = mean_sentiment_qdap > 0)) +
  geom_bar(stat = "identity") +
  coord_flip() +
  labs(title = "Distribución del Sentimiento QDAP por Estado", x = "Estado", y = "Puntuación Media de Sentimiento QDAP") +
  scale_fill_manual(values = c("red", "blue"), guide = FALSE)

# Harvard IV-4 (HE)
ggplot(sentiment_summary_he, aes(x = reorder(State, mean_sentiment_he), y = mean_sentiment_he, fill = mean_sentiment_he > 0)) +
  geom_bar(stat = "identity") +
  coord_flip() +
  labs(title = "Distribución del Sentimiento Harvard IV-4 por Estado", x = "Estado", y = "Puntuación Media de Sentimiento Harvard IV-4") +
  scale_fill_manual(values = c("red", "blue"), guide = FALSE)

# General Inquirer (GI)
ggplot(sentiment_summary_gi, aes(x = reorder(State, mean_sentiment_gi), y = mean_sentiment_gi, fill = mean_sentiment_gi > 0)) +
  geom_bar(stat = "identity") +
  coord_flip() +
  labs(title = "Distribución del Sentimiento General Inquirer por Estado", x = "Estado", y = "Puntuación Media de Sentimiento General Inquirer") +
  scale_fill_manual(values = c("red", "blue"), guide = FALSE)

Sentimiento QDAP

En la primera gráfica de distribución del sentimiento QDAP por estado, observamos que la mayoría de los estados tienen puntuaciones medias de sentimiento negativas. California y Kentucky destacan con las puntuaciones más negativas, mientras que algunos estados como Massachusetts y Nebraska muestran puntuaciones ligeramente positivas. Esto sugiere que, en general, los resúmenes de los tiroteos tienen un tono negativo, con algunas variaciones según el estado.

Sentimiento Harvard IV-4

La segunda gráfica muestra la distribución del sentimiento según el diccionario Harvard IV-4. Aquí, vemos que Kansas, Mississippi y Nevada tienen puntuaciones medias de sentimiento positivas, lo que es inusual dado el contexto de los resúmenes. Georgia, por otro lado, presenta una puntuación ligeramente negativa. La mayoría de los estados se agrupan cerca del cero, indicando una neutralidad relativa en los sentimientos según este diccionario.

Sentimiento General Inquirer

En la tercera gráfica, que utiliza el diccionario General Inquirer, todos los estados excepto Oklahoma muestran puntuaciones medias de sentimiento negativas. Indiana y California destacan con las puntuaciones más negativas.

Ahora usamos AFINN, un léxico de análisis de sentimientos AFINN es popular para el análisis de sentimientos en textos cortos:

# Asegurarnos de que el campo Summary está disponible
text_summary_state <- datos_limpios %>%
  select(State, Summary)

# Transformamos el texto de los resúmenes en un formato 'tidy', dividiéndolo en palabras individuales
tidy_text_summary_state <- text_summary_state %>%
  unnest_tokens(word, Summary)

# Realizamos un análisis de sentimiento utilizando el diccionario 'afinn'
sentiment_state <- tidy_text_summary_state %>% 
  inner_join(get_sentiments("afinn"), by = "word") %>%
  group_by(State) %>%    # Agrupamos por estado 
  summarise(sentiment = sum(value)) %>%
  ungroup()

# Ordenamos los resultados del análisis de sentimientos
datos_sentiment_state_sorted <- sentiment_state %>% 
  arrange(desc(sentiment))

# Mostrar un resumen de los datos agrupados
print(summary(datos_sentiment_state_sorted))
##     State             sentiment      
##  Length:58          Min.   :-239.00  
##  Class :character   1st Qu.: -41.00  
##  Mode  :character   Median : -19.00  
##                     Mean   : -34.05  
##                     3rd Qu.:  -6.25  
##                     Max.   :   1.00
# Graficamos la distribución del sentimiento en los resúmenes por estado
ggplot(data = datos_sentiment_state_sorted, aes(x = sentiment)) + 
  geom_histogram(bins = 50, color = 'firebrick', fill = 'darkred') + 
  scale_x_continuous(breaks = seq(min(datos_sentiment_state_sorted$sentiment), max(datos_sentiment_state_sorted$sentiment), by = 15)) +
  xlab("Sentimiento") + 
  ylab("Cantidad de Resúmenes") + 
  ggtitle("Distribución de Sentimiento en Resúmenes de Tiroteos por Estado")

La mayoría de los resúmenes tienen un tono negativo, con picos con valores negativos muy altos. Los sentimientos negativos alcanzan un valor mínimo de aproximadamente -239, sugiriendo que algunos resúmenes contienen una muy fuerte carga de términos negativos.

Voy a analizar los resúmenes de los tiroteos correspondientes a los sentimientos -239 y -213, que son con diferencias los más negativos:

# Informacion de los tiroteo con sentimiento -239
tiroteos_negative239 <- datos_sentiment_state_sorted %>%
  filter(sentiment == -239)

print(tiroteos_negative239)
## # A tibble: 1 × 2
##   State      sentiment
##   <chr>          <dbl>
## 1 California      -239

Los tiroteos de California tienen un sentimiento negativo muy alto, lo cual concuerda con la información que hemos ido obteniendo a lo largo del trabajo, como ha sido en el apartado donde hemos estudiado los tiroteos en 2015 y obtuvimos que en ese año hubo incidentes particularmente graves en California, que presentaba el círculo más grande en el mapa interactivo.

# Informacion de los tiroteos con sentimiento -213
tiroteos_negative213 <- datos_sentiment_state_sorted %>%
  filter(sentiment == -213)

print(tiroteos_negative213)
## # A tibble: 1 × 2
##   State sentiment
##   <chr>     <dbl>
## 1 <NA>       -213

Los tiroteos con estado no especificado tienen un sentimiento negativo muy alto. Más tarde, gracias a Deep LLMs, trataremos de obtener la localización de algunos tiroteos, que aparece en los resúmenes pero no en la columna de Location.

SNA para Análisis de Targets

He decidido realizar un Social Network Analysis para analizar datos textuales de tiroteos, específicamente analizando el campo “Target” para identificar y visualizar las relaciones entre diferentes tiroteos basadas en sus objetivos (targets) compartidos.

Creación del Grafo

Preparación de Datos y Creación del Corpus:

# Convertimos las columnas 'Target' y 'Title' del dataframe a tipo string para procesamiento de texto
datos_limpios$Target <- as.character(datos_limpios$Target)
datos_limpios$Title <- as.character(datos_limpios$Title)

# Creamos un corpus de texto a partir de la columna 'Target' y asociamos cada texto con su título correspondiente del tiroteo
shootings_target_corpus <- corpus(datos_limpios$Target, 
                           docvars = data.frame(Title = datos_limpios$Title,
                                        Target = datos_limpios$Target))

Procesamiento de Texto - Tokenización y Filtrado:

# Generamos tokens de cada texto en el corpus 
tokens_shootings_target <- tokens(shootings_target_corpus, what = "word")

# Eliminamos las palabras comunes (stopwords) para limpiar los datos
tokens_filtered_target <- tokens_select(tokens_shootings_target, 
                                 pattern = stopwords("english"), 
                                 selection = "remove")

Creación de Matriz Documento-Término:

# Creamos una matriz de frecuencia de términos donde cada fila es un documento y cada columna es un término
dfm_shootings_target <- dfm(tokens_filtered_target)

Conversión a Matriz de Presencia/Ausencia:

# Convertimos la matriz de frecuencia en una matriz de presencia/ausencia (1 si el término está presente, 0 si no lo está)
dfm_shootings_target[dfm_shootings_target >= 1] <- 1

Creación de Matriz de Adyacencia Término-Término:

# Generamos una matriz de adyacencia término-término basada en la co-ocurrencia de términos en los documentos
termMatrix_target <- dfm_shootings_target %*% t(dfm_shootings_target)

Creación del grafo:

# Creamos un grafo a partir de la matriz de adyacencia, utilizando un modo no dirigido y asignando pesos a las aristas
g_target <- graph_from_adjacency_matrix(termMatrix_target, 
                                        mode = "undirected", 
                                        weighted = TRUE)

# Simplificamos el grafo eliminando aristas múltiples y loops para clarificar la visualización
g_target <- simplify(g_target, remove.multiple = TRUE, remove.loops = TRUE)

Configuración del grafo y asignaciones:

# Asignamos etiquetas y calculamos el grado de cada vértice en el grafo para análisis posterior
V(g_target)$label <- V(g_target)$name
V(g_target)$degree <- degree(g_target)

# Asignamos el título del tiroteo y el target a cada vértice para proporcionar contexto adicional en el análisis del grafo
V(g_target)$title <- docvars(shootings_target_corpus)$Title
V(g_target)$target <- docvars(shootings_target_corpus)$Target

Filtrado del grafo:

# Filtramos el grafo, eliminando aristas con un peso inferior a un umbral definido para enfocar en conexiones más fuertes
threshold <- 2
g_filtered_target <- delete_edges(g_target, E(g_target)[weight < threshold])

# Eliminamos vértices que se quedaron sin conexiones después de la eliminación de aristas
g_filtered_target <- delete_vertices(g_filtered_target, 
                            V(g_filtered_target)[degree(g_filtered_target) == 0])

Exportación del grafo:

# Exportamos el grafo filtrado a un archivo GraphML para su uso en Gephi
write_graph(g_filtered_target, "target_network.graphml", format = "graphml")

Calcular Medidas de Centralidad

Las medidas de centralidad son métricas utilizadas en el análisis de redes para determinar la importancia o el prestigio de los nodos dentro de una red. Estas medidas ayudan a identificar los nodos más influyentes, los puntos críticos de comunicación, o los intermediarios clave en la red.

Centralidad de Grado (Degree)

Mide cuántas conexiones (aristas) tiene un nodo.

# Calculamos la centralidad de grado
degree_centrality <- degree(g_filtered_target, mode = "all")

# Creamos un dataframe que contenga la información relevante
tiroteos_df_degree <- data.frame(
  Title = V(g_filtered_target)$title,
  Target = V(g_filtered_target)$target,
  DegreeCentrality = degree_centrality,
  stringsAsFactors = FALSE
)

# Ordenamos por centralidad de grado para ver primero los más conectados
tiroteos_df_degree <- tiroteos_df_degree[order(-tiroteos_df_degree$DegreeCentrality),]

# Imprimimos el dataframe
print(tiroteos_df_degree)
##                                                Title                 Target
## text177                 Sandy Hook Elementary School        Family+students
## text219                          SuccessTech Academy      Students+Teachers
## text238              Case Western Reserve University      Students+Teachers
## text240                    Appalachian School of Law      Students+Teachers
## text250                        Columbine High School      Students+Teachers
## text252                         Thurston High School       Students+Parents
## text254              Westside Middle School killings      Students+Teachers
## text261                  Bethel Regional High School      Students+Teachers
## text264                  Frontier Junior High School      Students+Teachers
## text265                         Richland High School      Students+Teachers
## text282                 Simon's Rock College of Bard      Students+Teachers
## text285                        Lindhurst High School      Students+Teachers
## text294                  Cleveland Elementary School      Students+Teachers
## text296                    Oakland Elementary School      Students+Teachers
## text306                   Goddard Junior High School      Students+Teachers
## text313                           Valley High School      Students+Teachers
## text33                           Plantation, Florida           party guests
## text45    Chelsea, MA empty apartment party shooting           party guests
## text76            Gloucester County, VA, House Party           party guests
## text82                    San Bernardino, California           party guests
## text108                                Miami Gardens           party guests
## text125                            Panama City Beach           party guests
## text146                                      Memphis           party guests
## text220                             Crandon shooting           party guests
## text141                                 McAvan's Pub Ex-Girlfriend & Family
## text147 Pennsburg, Souderton, Lansdale, Harleysville       Ex-Wife & Family
## text160                   Cedarville Rancheria Tribe          Family+random
## text194                   Residences in Grand Rapids       Ex-Wife & Family
## text195       Southern Union State Community College       Ex-Wife & Family
## text148                                   Morgantown   Ex-Girlfriend+random
## text161                  Centennial Hill Bar & Grill          rapper+random
## text224                        Amish school shooting           school girls
## text225               West Nickel Mines Amish School           school girls
##         DegreeCentrality
## text177               16
## text219               15
## text238               15
## text240               15
## text250               15
## text252               15
## text254               15
## text261               15
## text264               15
## text265               15
## text282               15
## text285               15
## text294               15
## text296               15
## text306               15
## text313               15
## text33                 7
## text45                 7
## text76                 7
## text82                 7
## text108                7
## text125                7
## text146                7
## text220                7
## text141                3
## text147                3
## text160                3
## text194                3
## text195                3
## text148                2
## text161                2
## text224                1
## text225                1

Los resultados mostrados en la tabla reflejan la centralidad de grado de cada nodo en un grafo de tiroteos, lo que indica el número de conexiones directas que cada evento de tiroteo tiene con otros en la red.

La centralidad de grado alta en tiroteos en escuelas, especialmente aquellos que involucran a “Students+Teachers” como target, sugiere que estos eventos no solo son frecuentes, sino también que tienden a compartir características o estar conectados con muchos otros eventos similares en la red. Esto puede indicar patrones comunes o motivaciones similares en tiroteos escolares.

Por otro lado, los eventos con objetivos como “party guests” o “Ex-Wife & Family” muestran una centralidad menor, lo que sugiere que, aunque estos tiroteos ocurren, tienen menos conexiones directas con otros eventos en la red. Esto podría interpretarse como que estos tiroteos son más aislados o menos frecuentes en términos de patrones similares en comparación con los tiroteos en escuelas.

Centralidad de tamaño de vecindario (Reach, neighborhood.size)

Medida de la capacidad de un nodo para conectar con otros nodos dentro de un cierto número de pasos.

# Calculamos el tamaño del vecindario hasta una distancia de 1, 2 y 3
neighborhood_size_1 <- neighborhood.size(g_filtered_target, 
                                         order = 1, mode = "all")
neighborhood_size_2 <- neighborhood.size(g_filtered_target, 
                                         order = 2, mode = "all")
neighborhood_size_3 <- neighborhood.size(g_filtered_target, 
                                         order = 3, mode = "all")

# Creamos un dataframe para almacenar estos resultados junto con títulos y targets
neighborhood_data <- data.frame(
  Title = V(g_filtered_target)$title,
  Target = V(g_filtered_target)$target,
  SizeOrder1 = neighborhood_size_1,
  SizeOrder2 = neighborhood_size_2,
  SizeOrder3 = neighborhood_size_3,
  stringsAsFactors = FALSE
)

# Ordenamos el dataframe por el tamaño del vecindario a la distancia de orden 3
ordered_data_neighborhood <- 
  neighborhood_data[order(-neighborhood_data$SizeOrder3),]

# Imprimimos los resultados
print(ordered_data_neighborhood)
##                                           Title                 Target
## 10                                   Morgantown   Ex-Girlfriend+random
## 11                   Cedarville Rancheria Tribe          Family+random
## 12                  Centennial Hill Bar & Grill          rapper+random
## 13                 Sandy Hook Elementary School        Family+students
## 16                          SuccessTech Academy      Students+Teachers
## 20              Case Western Reserve University      Students+Teachers
## 21                    Appalachian School of Law      Students+Teachers
## 22                        Columbine High School      Students+Teachers
## 23                         Thurston High School       Students+Parents
## 24              Westside Middle School killings      Students+Teachers
## 25                  Bethel Regional High School      Students+Teachers
## 26                  Frontier Junior High School      Students+Teachers
## 27                         Richland High School      Students+Teachers
## 28                 Simon's Rock College of Bard      Students+Teachers
## 29                        Lindhurst High School      Students+Teachers
## 30                  Cleveland Elementary School      Students+Teachers
## 31                    Oakland Elementary School      Students+Teachers
## 32                   Goddard Junior High School      Students+Teachers
## 33                           Valley High School      Students+Teachers
## 1                           Plantation, Florida           party guests
## 2    Chelsea, MA empty apartment party shooting           party guests
## 3            Gloucester County, VA, House Party           party guests
## 4                    San Bernardino, California           party guests
## 5                                 Miami Gardens           party guests
## 6                             Panama City Beach           party guests
## 8                                       Memphis           party guests
## 17                             Crandon shooting           party guests
## 7                                  McAvan's Pub Ex-Girlfriend & Family
## 9  Pennsburg, Souderton, Lansdale, Harleysville       Ex-Wife & Family
## 14                   Residences in Grand Rapids       Ex-Wife & Family
## 15       Southern Union State Community College       Ex-Wife & Family
## 18                        Amish school shooting           school girls
## 19               West Nickel Mines Amish School           school girls
##    SizeOrder1 SizeOrder2 SizeOrder3
## 10          3          4         19
## 11          4         19         19
## 12          3          4         19
## 13         17         19         19
## 16         16         17         19
## 20         16         17         19
## 21         16         17         19
## 22         16         17         19
## 23         16         17         19
## 24         16         17         19
## 25         16         17         19
## 26         16         17         19
## 27         16         17         19
## 28         16         17         19
## 29         16         17         19
## 30         16         17         19
## 31         16         17         19
## 32         16         17         19
## 33         16         17         19
## 1           8          8          8
## 2           8          8          8
## 3           8          8          8
## 4           8          8          8
## 5           8          8          8
## 6           8          8          8
## 8           8          8          8
## 17          8          8          8
## 7           4          4          4
## 9           4          4          4
## 14          4          4          4
## 15          4          4          4
## 18          2          2          2
## 19          2          2          2

La tabla presenta el tamaño del vecindario para cada tiroteo en tres órdenes de distancia, reflejando cómo el alcance de cada nodo (tiroteo) se expande en la red a medida que se consideran conexiones más distantes.

Los tiroteos relacionados con escuelas, especialmente aquellos con el target “Students+Teachers”, muestran un tamaño de vecindario considerablemente más amplio en todas las distancias, alcanzando el máximo en el tercer orden. Esto indica que estos eventos están muy conectados entre sí y con otros eventos, posiblemente debido a patrones o características compartidas que son comunes en tiroteos en entornos educativos.

Por otro lado, eventos con targets más específicos o menos comunes, como “party guests” y “Ex-Wife & Family”, tienen tamaños de vecindario más pequeños, indicando que estos tiroteos son más aislados dentro de la red.

Eventos aún más particulares, como aquellos dirigidos a “school girls”, presentan los vecindarios más pequeños, lo que sugiere que son incidentes muy específicos con pocas conexiones a otros eventos.

Centralidad de Cercanía (Closeness)

Mide la cercanía promedio de un nodo a todos los demás nodos en la red.

# Calculamos la centralidad de cercanía
closeness_centrality <- closeness(g_filtered_target, mode = "all")

# Creamos un dataframe que contenga la información relevante
tiroteos_df_closeness <- data.frame(
  Title = V(g_filtered_target)$title,
  Target = V(g_filtered_target)$target,
  ClosenessCentrality = closeness_centrality,
  stringsAsFactors = FALSE
)

# Ordenamos por centralidad de cercania para ver primero los más cercanos
tiroteos_df_closeness <- 
  tiroteos_df_closeness[order(-tiroteos_df_closeness$ClosenessCentrality),]

# Imprimimos el dataframe
print(tiroteos_df_closeness)
##                                                Title                 Target
## text224                        Amish school shooting           school girls
## text225               West Nickel Mines Amish School           school girls
## text141                                 McAvan's Pub Ex-Girlfriend & Family
## text147 Pennsburg, Souderton, Lansdale, Harleysville       Ex-Wife & Family
## text194                   Residences in Grand Rapids       Ex-Wife & Family
## text195       Southern Union State Community College       Ex-Wife & Family
## text33                           Plantation, Florida           party guests
## text45    Chelsea, MA empty apartment party shooting           party guests
## text76            Gloucester County, VA, House Party           party guests
## text82                    San Bernardino, California           party guests
## text108                                Miami Gardens           party guests
## text125                            Panama City Beach           party guests
## text146                                      Memphis           party guests
## text220                             Crandon shooting           party guests
## text177                 Sandy Hook Elementary School        Family+students
## text252                         Thurston High School       Students+Parents
## text219                          SuccessTech Academy      Students+Teachers
## text238              Case Western Reserve University      Students+Teachers
## text240                    Appalachian School of Law      Students+Teachers
## text250                        Columbine High School      Students+Teachers
## text254              Westside Middle School killings      Students+Teachers
## text261                  Bethel Regional High School      Students+Teachers
## text264                  Frontier Junior High School      Students+Teachers
## text265                         Richland High School      Students+Teachers
## text282                 Simon's Rock College of Bard      Students+Teachers
## text285                        Lindhurst High School      Students+Teachers
## text294                  Cleveland Elementary School      Students+Teachers
## text296                    Oakland Elementary School      Students+Teachers
## text306                   Goddard Junior High School      Students+Teachers
## text313                           Valley High School      Students+Teachers
## text160                   Cedarville Rancheria Tribe          Family+random
## text148                                   Morgantown   Ex-Girlfriend+random
## text161                  Centennial Hill Bar & Grill          rapper+random
##         ClosenessCentrality
## text224          0.50000000
## text225          0.50000000
## text141          0.16666667
## text147          0.12500000
## text194          0.12500000
## text195          0.12500000
## text33           0.07142857
## text45           0.07142857
## text76           0.07142857
## text82           0.07142857
## text108          0.07142857
## text125          0.07142857
## text146          0.07142857
## text220          0.07142857
## text177          0.02500000
## text252          0.02173913
## text219          0.01694915
## text238          0.01694915
## text240          0.01694915
## text250          0.01694915
## text254          0.01694915
## text261          0.01694915
## text264          0.01694915
## text265          0.01694915
## text282          0.01694915
## text285          0.01694915
## text294          0.01694915
## text296          0.01694915
## text306          0.01694915
## text313          0.01694915
## text160          0.01515152
## text148          0.01020408
## text161          0.01020408

La tabla muestra la centralidad de cercanía para tiroteos.

Los tiroteos que involucran a “school girls” y “Ex-Girlfriend & Family” muestran los valores más altos de centralidad de cercanía, indicando que estos eventos están más centrados y tienen caminos más cortos hacia otros eventos en la red. Esto podría sugerir que aunque sean menos comunes, ocupan posiciones estratégicas dentro de la estructura de la red.

Por otro lado, tiroteos en instituciones educativas con objetivos como “Students+Teachers” presentan valores relativamente bajos de centralidad de cercanía, lo que indica que, aunque están densamente conectados entre sí, están situados más periféricamente en términos de la estructura global de la red. Es decir, a pesar de su frecuencia y severidad, estos eventos podrían estar más aislados de otros tipos de tiroteos en la red.

Detectar Comunidades en el Grafo

Vamos a obtener las comunidades de este grafo que hemos creado:

# Detectamos comunidades usando el algoritmo de Louvain
communities <- cluster_louvain(g_filtered_target)

# Asignamos la comunidad a cada vértice como atributo
V(g_filtered_target)$community <- membership(communities)

# Asignamos el target como etiqueta de cada vértice para visualización
V(g_filtered_target)$label <- V(g_filtered_target)$target

# Creamos un dataframe para visualizar los vértices con sus respectivas comunidades
node_communities <- data.frame(
  Title = V(g_filtered_target)$title,
  Target = V(g_filtered_target)$target,
  Community = V(g_filtered_target)$community,
  stringsAsFactors = FALSE
)

# Ordenamos por comunidad para una mejor visualización
node_communities <- node_communities[order(node_communities$Community),]

# Imprimimos el dataframe
print(node_communities)
##                                           Title                 Target
## 1                           Plantation, Florida           party guests
## 2    Chelsea, MA empty apartment party shooting           party guests
## 3            Gloucester County, VA, House Party           party guests
## 4                    San Bernardino, California           party guests
## 5                                 Miami Gardens           party guests
## 6                             Panama City Beach           party guests
## 8                                       Memphis           party guests
## 17                             Crandon shooting           party guests
## 7                                  McAvan's Pub Ex-Girlfriend & Family
## 9  Pennsburg, Souderton, Lansdale, Harleysville       Ex-Wife & Family
## 14                   Residences in Grand Rapids       Ex-Wife & Family
## 15       Southern Union State Community College       Ex-Wife & Family
## 10                                   Morgantown   Ex-Girlfriend+random
## 11                   Cedarville Rancheria Tribe          Family+random
## 12                  Centennial Hill Bar & Grill          rapper+random
## 13                 Sandy Hook Elementary School        Family+students
## 16                          SuccessTech Academy      Students+Teachers
## 20              Case Western Reserve University      Students+Teachers
## 21                    Appalachian School of Law      Students+Teachers
## 22                        Columbine High School      Students+Teachers
## 23                         Thurston High School       Students+Parents
## 24              Westside Middle School killings      Students+Teachers
## 25                  Bethel Regional High School      Students+Teachers
## 26                  Frontier Junior High School      Students+Teachers
## 27                         Richland High School      Students+Teachers
## 28                 Simon's Rock College of Bard      Students+Teachers
## 29                        Lindhurst High School      Students+Teachers
## 30                  Cleveland Elementary School      Students+Teachers
## 31                    Oakland Elementary School      Students+Teachers
## 32                   Goddard Junior High School      Students+Teachers
## 33                           Valley High School      Students+Teachers
## 18                        Amish school shooting           school girls
## 19               West Nickel Mines Amish School           school girls
##    Community
## 1          1
## 2          1
## 3          1
## 4          1
## 5          1
## 6          1
## 8          1
## 17         1
## 7          2
## 9          2
## 14         2
## 15         2
## 10         3
## 11         3
## 12         3
## 13         4
## 16         4
## 20         4
## 21         4
## 22         4
## 23         4
## 24         4
## 25         4
## 26         4
## 27         4
## 28         4
## 29         4
## 30         4
## 31         4
## 32         4
## 33         4
## 18         5
## 19         5
# Mostramos las comunidades en el grafo
plot(communities, g_filtered_target,
     vertex.size = 10,
     vertex.color = V(g_filtered_target)$community,
     asp = FALSE, # Evitamos que el plot sea distorsionado en proporción
     main = "Visualización del Grafo con Comunidades")

La visualización del grafo muestra cómo los tiroteos están agrupados en comunidades basadas en la similitud de sus targets.

La comunidad más grande, coloreada en azul, incluye tiroteos en entornos educativos con targets como “Students+Teachers”, que indica que los tiroteos en escuelas son frecuentes y están estrechamente interconectados en términos de sus características.

La comunidad naranja agrupa eventos con targets relacionados a “party guests”, sugiriendo una conexión entre tiroteos en reuniones sociales. Estos eventos forman un grupo distinto pero menos extenso que el de las escuelas.

La comunidad verde, bastante pequeña en comparación con la azul y la naranja, muestra tiroteos que tienen como target “alguien + random”, por lo que podemos ver que se están agrupando por ese componente de random.

La comunidad amarilla, también pequeña, tiene como target a personas de la familia, como “ex-wife”, “ex-girlfriend” y directamente “family”, por lo que esta es una comunidad más del ámbito doméstico a diferencia de las demás.

Por último, la comunidad más pequeña, que es la rosa, presenta como target a “school girls”.

Analizar la Estructura del Grafo

Calculamos algunas métricas globales del grafo:

# Calculamos la densidad de la red
network_density <- graph.density(g_filtered_target)

# Calculamos el coeficiente de agrupamiento global
clustering_coefficient <- transitivity(g_filtered_target, type = "global")

# Mostramos las métricas de estructura
cat("Densidad de la Red:", network_density, "\n")
## Densidad de la Red: 0.3011364
cat("Coeficiente de Agrupamiento:", clustering_coefficient, "\n")
## Coeficiente de Agrupamiento: 0.9909574

Estos resultados sugieren que los tiroteos no son eventos aislados sino que tienden a ocurrir en contextos que comparten características, facilitando conexiones y agrupamientos entre ellos.

La densidad podría indicar que algunos tiroteos comparten múltiples características en común, permitiendo numerosas conexiones directas entre ellos.

El alto coeficiente de agrupamiento sugiere que estos tiroteos tienden a agruparse de forma que cada evento está potencialmente relacionado con muchos otros en su grupo, lo que podría reflejar factores comunes como el tipo de objetivo.

Deep LLMs - Proceso de Extracción y Limpieza de Ubicaciones en Resúmenes de Tiroteos

Al analizar los datos del dataset, me di cuenta de que había tiroteos en los que aparecía vacío el campo de Location, a pesar de mencionarse la ubicación del tiroteo en el campo Summary.

El objetivo principal de este apartado es identificar y extraer automáticamente las ubicaciones mencionadas en los resúmenes de incidentes de tiroteos, y posteriormente limpiar y corregir cualquier anomalía en los datos extraídos.

Una vez obtenidas estas ubicaciones, se añadirán en una nueva columna del dataframe para su uso en posteriores análisis, que servirá tanto para tener la ubicación de aquellos tiroteos con el campo Location vacío, como para tener un detalle más profundo de la ubicación del tiroteo en aquellos que sí tengan relleno el campo Location.

También, podremos comprobar si la ubicación extraída del resumen coincide con la localización de aquellos tiroteos que no tienen el campo Location vacío.

Para llevar a cabo esta tarea, hemos utilizado una combinación de herramientas de procesamiento de lenguaje natural (NLP) y técnicas de manipulación de datos.

A continuación, mencionamos las principales herramientas y métodos utilizados:

  1. Python y Pandas:

    • Utilizamos Python como el lenguaje principal para el procesamiento de datos.

    • La biblioteca Pandas se empleó para la carga, manipulación y almacenamiento de datos en formato CSV.

  2. Transformers de Hugging Face:

    • Empleamos el modelo dbmdz/bert-large-cased-finetuned-conll03-english, un modelo de transformers preentrenado para el reconocimiento de entidades nombradas (NER), capaz de identificar entidades como ubicaciones, nombres de personas y organizaciones en el texto.

    • La función pipeline de Hugging Face se utilizó para crear un pipeline de NER, que facilita la identificación y extracción de ubicaciones.

  3. Tokenización y Limpieza de Datos:

    • Los modelos de transformers a menudo descomponen palabras en subpalabras, marcando las continuaciones con un prefijo ##. Implementamos una lógica específica para unir estas subpalabras y formar palabras completas.

    • Creamos una función personalizada para procesar los tokens, eliminando caracteres no deseados y uniendo subpalabras adecuadamente.

  4. Procesamiento por Lotes:

    • Para manejar de manera eficiente el procesamiento de grandes volúmenes de texto, dividimos el dataset en lotes y procesamos cada lote de manera iterativa.

En el archivo DeepLLMs_ExtractLocations_Transformers_LauraLlorente_ProyectoFinalBAIN.ipynb adjuntado en la entrega, encontramos el código de las pruebas para obtener los nuevos ficheros CSV con la nueva columna de localización extraída de las ubicaciones mencionadas en los resúmenes de los tiroteos del dataset.

Se han obtenido las localizaciones de aquellos tiroteos que mencionaban la ubicación en el resumen. Si no es mencionada, no se añadirá la localización para ese tiroteo.

Prueba 1

Hemos hecho un código en Python que realiza un análisis de texto utilizando el modelo de reconocimiento de entidades nombradas (NER) de Hugging Face para extraer ubicaciones de los resúmenes de incidentes de tiroteos. El script carga el dataset, utiliza el modelo de transformers preentrenado para identificar y extraer las ubicaciones mencionadas en los resúmenes, y guarda los resultados en un nuevo archivo CSV.

Problema: Durante el proceso de extracción de ubicaciones utilizando modelos de transformers como BERT, es común encontrar caracteres ## delante de algunas palabras. Esto se debe al proceso de tokenización de subpalabras utilizado por estos modelos. Los tokens que comienzan con ## representan subpalabras que se deben unir a la palabra anterior. Por ejemplo, “Mandalay Bay” es tokenizado como “Man ##ada ##lay Bay”, por lo que debemos solucionar este problema para poder emplear bien los datos.

# Ruta donde tenemos guardado el fichero csv y el nombre del fichero
ruta_fichero_2 <- "D:/Laura - UTAD/Tercero/Segundo cuatrimestre/Búsqueda y Análisis de la Información/Tareas/Tarea final/"

fichero_2 <- "Extracted_Locations.csv"

# Leemos el archivo CSV 
  # 'paste0' se usa para construir la ruta completa dell archivo
  # 'header = TRUE' indica que la primera fila contiene los nombres de las columnas
  # 'sep = ","' especifica que el separador de campos en el archivo CSV es una coma

datos_2 <- read.csv(file = paste0(ruta_fichero_2, fichero_2), header = TRUE, sep = ",")

# Usamos subset para seleccionar solo las columnas de la localizacion
selected_data <- subset(datos_2, select = c(Location, Extracted_Location))

# Mostramos las primeras filas
head(selected_data)

En la comparación de las columnas “Location” y “Extracted_Location” del dataset, se observa que algunas localizaciones se han extraído correctamente mientras que otras presentan errores.

“Texas” y “San Francisco” se ha extraído correctamente del summary porque en Location tenemos “Sutherland Springs, TX” y “San Francisco, CA”.

Del tiroteo en “Las Vegas, NV” se ha extraído “Man ##ada ##lay Bay”, que es correcto porque Mandalay Bay es un casino de Las Vegas, pero tenemos que limpiar correctamente la localización para quitar los hashtags que hemos comentado antes.

“Edgewood, MD” tiene una combinación de “Baltimore Wilmington Delaware”.

# Filtramos las filas donde 'Extracted_Location' es 'Baltimore Wilmington Delaware'
tiroteo_filtrado <- subset(datos_2, 
                           Extracted_Location == "Baltimore Wilmington Delaware")

# Imprimimos el resultado
print(tiroteo_filtrado$Summary)
## [1] "Radee Labeeb Prince, 37, fatally shot three people and wounded two others around 9am at Advance Granite Solutions, a home remodeling business where he worked near Baltimore. Hours later he shot and wounded a sixth person at a car dealership in Wilmington, Delaware. He was apprehended that evening following a manhunt by authorities."

Podemos ver, con el resumen, que se han extraído bien las localizaciones mencionadas en el resumen, donde tuvieron lugar los tiroteos. Baltimore es una ciudad de Maryland, que aparece en Location como MD.

“Thornton, CO” aparece como “Denver”. Ambas son ciudades de Colorado (CO). Vamos a analizar cuál se menciona en el resumen del tiroteo:

# Filtramos las filas donde 'Extracted_Location' es 'Denver Denver'
tiroteo_filtrado <- subset(datos_2, 
                           Extracted_Location == "Denver Denver")

# Imprimimos el resultado
print(tiroteo_filtrado$Summary)
## [1] "Scott Allen Ostrem, 47, walked into a Walmart in a suburb north of Denver and fatally shot two men and a woman, then left the store and drove away. After an all-night manhunt, Ostrem, who had financial problems but no serious criminal history, was captured by police after being spotted near his apartment in Denver."

Podemos ver que la ciudad que se menciona en el resumen es Denver, que está cerca de Thornton, por lo que ha podido haber un error al rellenar ese campo de Location del dataset.

Prueba 2

Hemos hecho un código en Python que realiza un análisis de texto utilizando el modelo de reconocimiento de entidades nombradas (NER) de Hugging Face para extraer y limpiar ubicaciones de los resúmenes de incidentes de tiroteos. El script carga el dataset, utiliza un modelo de transformers preentrenado para identificar y extraer las ubicaciones mencionadas en los resúmenes, limpia los tokens de subpalabras, y guarda los resultados en un nuevo archivo CSV.

Problema: Hemos eliminado los caracteres ## delante de las palabras, pero ahora estas subpalabras no se unen adecuadamente y resultan en palabras separadas por espacios. Por ejemplo, “Man ##ada ##lay Bay” ha pasado a ser “Man ada lay Bay”, pero todavía no es “Mandalay Bay”, que es lo que buscamos para el correcto análisis en R de las localizaciones.

# Ruta donde tenemos guardado el fichero csv y el nombre del fichero
ruta_fichero_3 <- "D:/Laura - UTAD/Tercero/Segundo cuatrimestre/Búsqueda y Análisis de la Información/Tareas/Tarea final/"

fichero_3 <- "Extracted_Locations_Clean.csv"

# Leemos el archivo CSV 
  # 'paste0' se usa para construir la ruta completa dell archivo
  # 'header = TRUE' indica que la primera fila contiene los nombres de las columnas
  # 'sep = ","' especifica que el separador de campos en el archivo CSV es una coma
  
datos_3 <- read.csv(file = paste0(ruta_fichero_3, fichero_3), header = TRUE, sep = ",")

# Usamos subset para seleccionar solo las columnas de la localizacion
selected_data <- subset(datos_3, select = c(Location, Extracted_Location))

# Mostramos las primeras filas
head(selected_data)

Del tiroteo en “Las Vegas, NV” se ha extraído ahora “Man ada lay Bay”, que es correcto porque Mandalay Bay es un casino de Las Vegas, pero ahora que hemos limpiado los hashtags, se han quedado espacios entre la palabra, que tenemos que eliminar.

Prueba 3

Hemos hecho un código en Python que utiliza el modelo de reconocimiento de entidades nombradas (NER) de Hugging Face para extraer y limpiar ubicaciones de los resúmenes de incidentes de tiroteos. El script carga el dataset, utiliza un modelo de transformers preentrenado para identificar y extraer las ubicaciones mencionadas en los resúmenes, limpia los tokens de subpalabras, une las palabras correctamente y guarda los resultados en un nuevo archivo CSV.

Ya hemos solucionado todos los problemas surgidos y hemos obtenido, en aquellos tiroteos que mencionan la ubicación en el resumen, el lugar donde ocurrió el incidente.

# Ruta donde tenemos guardado el fichero csv y el nombre del fichero
ruta_fichero_4 <- "D:/Laura - UTAD/Tercero/Segundo cuatrimestre/Búsqueda y Análisis de la Información/Tareas/Tarea final/"

fichero_4 <- "Extracted_Locations_Clean_2.csv"

# Leemos el archivo CSV 
  # 'paste0' se usa para construir la ruta completa dell archivo
  # 'header = TRUE' indica que la primera fila contiene los nombres de las columnas
  # 'sep = ","' especifica que el separador de campos en el archivo CSV es una coma
  
datos_4 <- read.csv(file = paste0(ruta_fichero_4, fichero_4), header = TRUE, sep = ",")

# Usamos subset para seleccionar solo las columnas de la localizacion
selected_data <- subset(datos_4, select = c(Location, Extracted_Location))

# Mostramos las primeras filas
head(selected_data)

Del tiroteo en “Las Vegas, NV” se ha extraído ahora “Manadalay Bay”, que es correcto porque Mandalay Bay es un casino de Las Vegas, y ahora ya lo hemos limpiado y unido correctamente.

Al igual que hemos solucionado este problema de Manadalay Bay, también se han solucionado situaciones similares en otros tiroteos, obteniendo la localización extraída del resumen correctamente.

Análisis de la nueva columna Extracted_Location

Vamos a comprobar en cuántos tiroteos hemos conseguido obtener la localización a partir del resumen:

# Comparamos las columnas según coincidencias
total <- nrow(datos_4)
filled_extracted_location <- sum(datos_4$Extracted_Location != "")
empty_extracted_location <- sum(datos_4$Extracted_Location == "")

# Creamos un data frame para almacenar los resultados
match_summary <- data.frame(
  Total = total,
  Filled_Extracted_Location = filled_extracted_location,
  Empty_Extracted_Location = empty_extracted_location
)

# Imprimimos el resumen de coincidencias
print(match_summary)
##   Total Filled_Extracted_Location Empty_Extracted_Location
## 1   323                       205                      118

Podemos ver que de las 323 filas que tenía el dataset, hemos podido obtener localización a partir del resumen en 205 de ellas, lo cual es una buena cifra, ya que hay que tener en cuenta que hay resúmenes en los que no se menciona ninguna ubicación, y de los que por lo tanto no se habrá podido obtener información para la columna Extracted_Location.

Comparamos la columna Extracted_Location con la columna Location, que ya venía en el dataset:

# Convertimos a minúsculas y eliminamos espacios adicionales
df <- datos_4 %>%
  mutate(
    Location = str_trim(tolower(Location)),  
    Extracted_Location = str_trim(tolower(Extracted_Location))
  )

# Normalizamos las columnas eliminando comas y otros caracteres no significativos
df <- df %>%
  mutate(
    Normalized_Location = str_replace_all(Location, "[,]", ""),  
    Normalized_Extracted_Location = str_replace_all(Extracted_Location, "[,]", "")
  )

# Filtramos las filas donde 'Normalized_Location' y 'Normalized_Extracted_Location' no coinciden y seleccionamos solo las columnas originales 'Location' y 'Extracted_Location'
no_match <- df %>%
  filter(!str_detect(Normalized_Location, Normalized_Extracted_Location)) %>%
  select(Location, Extracted_Location)

# Mostramos las filas que no coinciden
print(no_match)
##                                                       Location
## 1                                       sutherland springs, tx
## 2                                                 thornton, co
## 3                                                 edgewood, md
## 4                                                las vegas, nv
## 5                                     fort lauderdale, florida
## 6                                               burlington, wa
## 7                                              baton rouge, la
## 8                                                             
## 9                                                             
## 10                                                            
## 11                                        louisville, kentucky
## 12                                                            
## 13                                         kansas city, kansas
## 14                                                            
## 15                                           glendale, arizona
## 16                                                            
## 17                                           iuka, mississippi
## 18                                                            
## 19                                                            
## 20                                                            
## 21                                                            
## 22                                  san bernardino, california
## 23                                      minneapolis, minnesota
## 24                                       jacksonville, florida
## 25                                              oakland, maine
## 26                                            roseburg, oregon
## 27                                         rochester, new york
## 28                                        lafayette, louisiana
## 29                                      morven, north carolina
## 30                                  charleston, south carolina
## 31                                           decatur, illinois
## 32                                          newark, new jersey
## 33                                        milwaukee, wisconsin
## 34                                             gates, new york
## 35                                              killeen, texas
## 36                                               rome, georgia
## 37                                      daytona beach, florida
## 38                                    little water, new mexico
## 39                                              killeen, texas
## 40                                    new port richey, florida
## 41                                   monroeville, pennsylvania
## 42                                          syracuse, new york
## 43  pennsburg, souderton, lansdale, harleysville, pennsylvania
## 44                                      marysville, washington
## 45                                      new orleans, louisiana
## 46                                   santa barbara, california
## 47                                            fort hood, texas
## 48                                              killeen, texas
## 49                                     los angeles, california
## 50                                             washington d.c.
## 51                                    salisbury , pennsylvania
## 52                                            mohawk, new york
## 53                                          irvine, california
## 54                       south valley, albuquerque, new mexico
## 55                                        newtown, connecticut
## 56                                        happy valley, oregon
## 57                                       brookfield, wisconsin
## 58                                              miami, florida
## 59                                        oak creek, wisconsin
## 60                                               chardon, ohio
## 61                                             tucson, arizona
## 62                                        parkland, washington
## 63                                        lakewood, washington
## 64                                            fort hood, texas
## 65                                             geneva, alabama
## 66                                          kirkwood, missouri
## 67                                           las vegas, nevada
## 68                                            arvada, colorado
## 69                                             omaha, nebraska
## 70                                        salt lake city, utah
## 71                              lancaster county, pennsylvania
## 72                       nickel mines, lancaster, pennsylvania
## 73                                hillsborough, north carolina
## 74                                         seattle, washington
## 75                                          goleta, california
## 76                                         red lake, minnesota
## 77                                       brookfield, wisconsin
## 78                                                tyler, texas
## 79                                              columbus, ohio
## 80                                              tampa, florida
## 81                                       fort gibson, oklahoma
## 82                                         littleton, colorado
## 83                                       riverside, california
## 84                                         springfield, oregon
## 85                                          pearl, mississippi
## 86                                              bethel, alaska
## 87                                        lynnville, tennessee
## 88                                             wickliffe, ohio
## 89                        fairchild air force base, washington
## 90                                           chelsea, michigan
## 91                                       garden city, new york
## 92                                fayetteville, north carolina
## 93                                                 ogden, utah
## 94                                      watkins glen, new york
## 95                                      olivehurst, california
## 96                                       escondido, california
## 97                                        stockton, california
## 98                                   greenwood, south carolina
## 99                                           chicago, illinois
## 100                                         winnetka, illinois
## 101                                         lewistown, montana
## 102                                            goddard, kansas
## 103                                              dallas, texas
## 104                                    los angeles, california
## 105                                   johnston, south carolina
## 106                                        seattle, washington
## 107                                          las vegas, nevada
## 108                                      san diego, california
## 109                                          chicago, illinois
## 110                                     new orleans, louisiana
## 111                                        spokane, washington
## 112                                              austin, texas
##                                              Extracted_Location
## 1                                                         texas
## 2                                                 denver denver
## 3                                 baltimore wilmington delaware
## 4                                                 manadalay bay
## 5                                        alaska fort lauderdale
## 6                cascade mall harborview medical center seattle
## 7                                                   iraq dallas
## 8                                                       alabama
## 9                                           south shore chicago
## 10                                                      alabama
## 11                                                         iraq
## 12                                                   trenton nj
## 13                                  kansas city kansas missouri
## 14                                             mlk lafayette la
## 15                                                      phoenix
## 16                                                 hazelwood mo
## 17                                             mississippi iuka
## 18                                                tampa florida
## 19                        mexican village nightlub rochester ny
## 20                                                  new orleans
## 21                                     avalon south los angeles
## 22                                       inland regional center
## 23                                                      4th pre
## 24                                            northside florida
## 25                                                central maine
## 26                                           oregon snyder hall
## 27                                               genesee street
## 28                     the grand 16 theater lafayette louisiana
## 29                                               boom boom room
## 30                 emanuel ame church charleston south carolina
## 31                                                   club maxey
## 32                                south 16th street 19th avenue
## 33                 emanuel ame church charleston south carolina
## 34                                                    gates pub
## 35                                      village west apartments
## 36                                  copeland st copeland street
## 37                                  daytona beach daytona beach
## 38                                           new mexico arizona
## 39                                                    fort hood
## 40                                                    moon lake
## 41                                             monroeville mall
## 42                                               mcavan ' s pub
## 43                                       lansdale lower salford
## 44                                            marysville pilchu
## 45                                               bourbon street
## 46                                     isla vista santa barbara
## 47                                    fort hood army post texas
## 48                                                    fort hood
## 49  terminal 3 los angeles international airport lax california
## 50                                     navy yard washington d c
## 51                                  ross salisbury pennsylvania
## 52                                     mohawk new york herkimer
## 53                   irvine california san bernardino mountains
## 54                                      south valley new mexico
## 55                               newtown connecticut sandy hook
## 56                clackamas town center city of portland oregon
## 57                                  aa spa brookfield wisconsin
## 58                                                          the
## 59                                   temple oak creek wisconsin
## 60                                         chardon chardon ohio
## 61                                                       ariz .
## 62                                                      seattle
## 63                                  lakewood washington seattle
## 64                            fort hood army base killeen texas
## 65                                        geneva county alabama
## 66                                  city hall kirkwood missouri
## 67                        ja las vegas nevada denver co chicago
## 68                              arvada colorado new life church
## 69                                               westroads mall
## 70                           salt lake city utah trolley square
## 71                                                bart township
## 72                     west nickel am nickel mines pennsylvania
## 73                           orange hillsborough north carolina
## 74                                         capitol hill seattle
## 75                              goleta office goleta california
## 76                                      lake red lake minnesota
## 77                        brookfield wisconsin living church of
## 78                           smith county courthousetyler texas
## 79                             alrosa villa night columbus ohio
## 80                                     bay harbor tampa florida
## 81                             fort gibson fort gibson oklahoma
## 82                                    umbine littleton colorado
## 83                               city hall riverside california
## 84                                     hurst springfield oregon
## 85                                      pearl pearl mississippi
## 86                                bethel regional bethel alaska
## 87                                 richland lynnville tennessee
## 88                                     wickliffe wickliffe ohio
## 89         fairchild air force base hospital spokane washington
## 90                                     chelsea chelsea michigan
## 91                                       nassau county new york
## 92                   ' s restaurant fayetteville north carolina
## 93                                                   odgen utah
## 94                        scyler building watkins glen new york
## 95                                  dhurs olivehurst california
## 96                      orange glen office escondido california
## 97                                cleveland stockton california
## 98                             oakland greenwood south carolina
## 99                                    chicago illinois monteore
## 100                                               hubbard woods
## 101                                    fergus lewistown montana
## 102                              goddard goddard kansas goddard
## 103                                           club dallas texas
## 104            49th street south central los angeles california
## 105                              johnston office south carolina
## 106                                  wah mee seattle washington
## 107                                     valley las vegas nevada
## 108                                 grover cleveland elementary
## 109                               clara barton chicago illinois
## 110                               downtown howard johnson hotel
## 111                         st . aloysius roman catholic church
## 112             austin tower of the university u s austin texas

Hay que tener en cuenta que, en algunos de estos casos, el campo Location está vacío, por lo que no sale coincidencia con la Extracted_Location, por lo que esos casos no los contamos como no coincidencia.

Además, he observado que muchas de las localizaciones extraídas que han salido como que no coinciden, no están mal, pero suelen ser más detalladas que la ubicación que aparece en Location.

Un ejemplo, es el caso que hemos comentado antes de Las Vegas. Aparece como que no coincide, pero la ubicación está bien extraída. Lo que ocurre es que en la columna Location del dataset aparece una localización más general, que es Las Vegas, y en Extracted_Location aparece una más específica, que es el casino Manadalay Bay, donde tuvo lugar el tiroteo.

Es decir, esta columna Extracted_Location nos sirve para poder buscar información más detallada y específica acerca de la ubicación de los tiroteos.

Mirando los datos de no coincidencias por encima, la única que me ha llamado más la atención es la siguiente, que sí se ha extraído incorrectamente como “the”:

# Filtramos las filas donde 'Extracted_Location' es 'the'
tiroteo_filtrado <- subset(no_match, 
                           Extracted_Location == "the")

# Imprimimos el resultado
print(tiroteo_filtrado)
##          Location Extracted_Location
## 58 miami, florida                the

Conclusiones

En el análisis de los tiroteos en Estados Unidos, he empleado diversas técnicas text mining, análisis de redes sociales (SNA) y modelos de lenguaje profundo (LLMs). Estas técnicas me han permitido extraer información valiosa de los datos y ofrecer una comprensión más profunda de los eventos.

Text Mining

Se utilizaron herramientas como quanteda para el análisis cuantitativo de textos, tidytext para manipulación y análisis de datos ordenados, y topicmodels para la modelización de tópicos, incluyendo el análisis de Latent Dirichlet Allocation (LDA). Además, el uso de SentimentAnalysis permitió evaluar los sentimientos y emociones asociados con los textos descriptivos de los incidentes. Estas técnicas ayudaron a identificar patrones recurrentes y características específicas de los tiroteos, como las motivaciones declaradas y los estados mentales de los atacantes.

Análisis de Redes Sociales (SNA)

El análisis de redes sociales facilitó la visualización de las conexiones entre diferentes incidentes, a través de su relación por el target. También, hemos podido encontrar comunidades de este grafo y ver cuáles eran las más incidentes en la red generada.

Modelos de Lenguaje Profundo (LLMs)

Se emplearon modelos de lenguaje profundo que permitieron obtener, a partir del resumen del tiroteo, la ubicación de este. Esto, no solo nos permite tener una ubicación para aquellos tiroteos que no tenían relleno el campo de Location, sino que también nos permite tener una información más detallada y específica del lugar de los hechos, que no solo se centra en ciudades o estados, sino que encontramos establecimientos, edificios, etc.